@tinacms/schema-tools 1.4.7 → 1.4.8

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.
package/dist/index.es.js CHANGED
@@ -146,13 +146,16 @@ const parseURL = (url) => {
146
146
  }
147
147
  const pattern = new UrlPattern("/:v/content/:clientId/github/*", {
148
148
  escapeChar: " ",
149
+ // extend to allow `.` in version
149
150
  segmentValueCharset: "a-zA-Z0-9-_~ %."
150
151
  });
151
152
  const result = pattern.match(params.pathname);
152
153
  const branch = result == null ? void 0 : result._;
153
154
  const clientId = result == null ? void 0 : result.clientId;
154
155
  if (!branch || !clientId) {
155
- throw new Error(`Invalid URL format provided. Expected: https://content.tinajs.io/<Version>/content/<ClientID>/github/<Branch> but but received ${url}`);
156
+ throw new Error(
157
+ `Invalid URL format provided. Expected: https://content.tinajs.io/<Version>/content/<ClientID>/github/<Branch> but but received ${url}`
158
+ );
156
159
  }
157
160
  return {
158
161
  host: params.host,
@@ -163,6 +166,9 @@ const parseURL = (url) => {
163
166
  };
164
167
  const normalizePath = (filepath) => filepath.replace(/\\/g, "/");
165
168
  class TinaSchema {
169
+ /**
170
+ * Create a schema class from a user defined schema object
171
+ */
166
172
  constructor(config) {
167
173
  this.config = config;
168
174
  this.getIsTitleFieldName = (collection) => {
@@ -172,10 +178,14 @@ class TinaSchema {
172
178
  return field == null ? void 0 : field.name;
173
179
  };
174
180
  this.getCollectionsByName = (collectionNames) => {
175
- return this.schema.collections.filter((collection) => collectionNames.includes(collection.name));
181
+ return this.schema.collections.filter(
182
+ (collection) => collectionNames.includes(collection.name)
183
+ );
176
184
  };
177
185
  this.getCollection = (collectionName) => {
178
- const collection = this.schema.collections.find((collection2) => collection2.name === collectionName);
186
+ const collection = this.schema.collections.find(
187
+ (collection2) => collection2.name === collectionName
188
+ );
179
189
  if (!collection) {
180
190
  throw new Error(`Expected to find collection named ${collectionName}`);
181
191
  }
@@ -190,6 +200,7 @@ class TinaSchema {
190
200
  break;
191
201
  }
192
202
  return {
203
+ // @ts-ignore FIXME: backwards compatibility, using `slug` should probably be deprecated
193
204
  slug: collection.name,
194
205
  ...extraFields,
195
206
  ...collection,
@@ -197,7 +208,9 @@ class TinaSchema {
197
208
  };
198
209
  };
199
210
  this.getCollections = () => {
200
- return this.schema.collections.map((collection) => this.getCollection(collection.name)) || [];
211
+ return this.schema.collections.map(
212
+ (collection) => this.getCollection(collection.name)
213
+ ) || [];
201
214
  };
202
215
  this.getCollectionByFullPath = (filepath) => {
203
216
  const fileExtension = filepath.split(".").pop();
@@ -238,19 +251,27 @@ class TinaSchema {
238
251
  const templates = this.getTemplatesForCollectable(collection);
239
252
  if (templates.type === "union") {
240
253
  if (templateName) {
241
- template = templates.templates.find((template2) => lastItem(template2.namespace) === templateName);
254
+ template = templates.templates.find(
255
+ (template2) => lastItem(template2.namespace) === templateName
256
+ );
242
257
  if (!template) {
243
- throw new Error(`Unable to determine template for item at ${filepath}`);
258
+ throw new Error(
259
+ `Unable to determine template for item at ${filepath}`
260
+ );
244
261
  }
245
262
  } else {
246
- throw new Error(`Unable to determine template for item at ${filepath}, no template name provided for collection with multiple templates`);
263
+ throw new Error(
264
+ `Unable to determine template for item at ${filepath}, no template name provided for collection with multiple templates`
265
+ );
247
266
  }
248
267
  }
249
268
  if (templates.type === "object") {
250
269
  template = templates.template;
251
270
  }
252
271
  if (!template) {
253
- throw new Error(`Something went wrong while trying to determine template for ${filepath}`);
272
+ throw new Error(
273
+ `Something went wrong while trying to determine template for ${filepath}`
274
+ );
254
275
  }
255
276
  return { collection, template };
256
277
  };
@@ -263,10 +284,17 @@ class TinaSchema {
263
284
  case "object":
264
285
  return templateInfo.template;
265
286
  case "union":
266
- assertShape(data, (yup2) => yup2.object({ _template: yup2.string().required() }));
267
- const template = templateInfo.templates.find((template2) => template2.namespace[template2.namespace.length - 1] === data._template);
287
+ assertShape(
288
+ data,
289
+ (yup2) => yup2.object({ _template: yup2.string().required() })
290
+ );
291
+ const template = templateInfo.templates.find(
292
+ (template2) => template2.namespace[template2.namespace.length - 1] === data._template
293
+ );
268
294
  if (!template) {
269
- throw new Error(`Expected to find template named '${data._template}' for collection '${lastItem(collection.namespace)}'`);
295
+ throw new Error(
296
+ `Expected to find template named '${data._template}' for collection '${lastItem(collection.namespace)}'`
297
+ );
270
298
  }
271
299
  return template;
272
300
  }
@@ -320,7 +348,10 @@ class TinaSchema {
320
348
  if (field.type === "object")
321
349
  if (field.templates) {
322
350
  if (field.list) {
323
- assertShape(value, (yup2) => yup2.array(yup2.object({ _template: yup2.string().required() })));
351
+ assertShape(
352
+ value,
353
+ (yup2) => yup2.array(yup2.object({ _template: yup2.string().required() }))
354
+ );
324
355
  return value.map((item) => {
325
356
  const { _template, ...rest } = item;
326
357
  const template = field.templates.find((template2) => {
@@ -337,7 +368,10 @@ class TinaSchema {
337
368
  };
338
369
  });
339
370
  } else {
340
- assertShape(value, (yup2) => yup2.object({ _template: yup2.string().required() }));
371
+ assertShape(
372
+ value,
373
+ (yup2) => yup2.object({ _template: yup2.string().required() })
374
+ );
341
375
  const { _template, ...rest } = value;
342
376
  return { [_template]: this.transformCollectablePayload(rest, field) };
343
377
  }
@@ -368,7 +402,7 @@ class TinaSchema {
368
402
  return false;
369
403
  };
370
404
  this.getTemplatesForCollectable = (collection) => {
371
- let extraFields = [];
405
+ const extraFields = [];
372
406
  if (collection == null ? void 0 : collection.fields) {
373
407
  const template = collection;
374
408
  if (typeof template.fields === "string" || typeof template.fields === "undefined") {
@@ -396,7 +430,11 @@ class TinaSchema {
396
430
  })
397
431
  };
398
432
  } else {
399
- throw new Error(`Expected either fields or templates array to be defined on collection ${collection.namespace.join("_")}`);
433
+ throw new Error(
434
+ `Expected either fields or templates array to be defined on collection ${collection.namespace.join(
435
+ "_"
436
+ )}`
437
+ );
400
438
  }
401
439
  }
402
440
  };
@@ -442,6 +480,12 @@ class TinaSchema {
442
480
  }
443
481
  });
444
482
  }
483
+ /**
484
+ * This function returns an array of glob matches for a given collection.
485
+ *
486
+ * @param collection The collection to get the matches for. Can be a string or a collection object.
487
+ * @returns An array of glob matches.
488
+ */
445
489
  getMatches({
446
490
  collection: collectionOrString
447
491
  }) {
@@ -536,6 +580,7 @@ const resolveField = (field, schema) => {
536
580
  }
537
581
  if (field.list) {
538
582
  return {
583
+ // Allows component to be overridden for scalars
539
584
  component: "list",
540
585
  field: {
541
586
  component: "text"
@@ -545,6 +590,7 @@ const resolveField = (field, schema) => {
545
590
  };
546
591
  }
547
592
  return {
593
+ // Allows component to be overridden for scalars
548
594
  component: "text",
549
595
  ...field,
550
596
  ...extraFields
@@ -555,7 +601,9 @@ const resolveField = (field, schema) => {
555
601
  return {
556
602
  ...field,
557
603
  component: field.list ? "group-list" : "group",
558
- fields: templateInfo.template.fields.map((field2) => resolveField(field2, schema)),
604
+ fields: templateInfo.template.fields.map(
605
+ (field2) => resolveField(field2, schema)
606
+ ),
559
607
  ...extraFields
560
608
  };
561
609
  } else if (templateInfo.type === "union") {
@@ -646,7 +694,9 @@ const parseZodError = ({ zodError }) => {
646
694
  moreInfo.push(parseZodError({ zodError: unionError }));
647
695
  });
648
696
  }
649
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(".")}`;
697
+ const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
698
+ "."
699
+ )}`;
650
700
  const errorMessages = [errorMessage, ...moreInfo];
651
701
  return {
652
702
  errors: errorMessages
@@ -692,18 +742,23 @@ const TypeName = [
692
742
  "rich-text"
693
743
  ];
694
744
  const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
695
- const typeRequiredError = `type is required and must be one of ${TypeName.join(", ")}`;
696
- const Option = z.union([
697
- z.string(),
698
- z.object({ label: z.string(), value: z.string() }),
699
- z.object({ icon: z.any(), value: z.string() })
700
- ], {
701
- errorMap: () => {
702
- return {
703
- message: "Invalid option array. Must be a string[] or {label: string, value: string}[] or {icon: React.ComponentType<any>, value: string}[]"
704
- };
745
+ const typeRequiredError = `type is required and must be one of ${TypeName.join(
746
+ ", "
747
+ )}`;
748
+ const Option = z.union(
749
+ [
750
+ z.string(),
751
+ z.object({ label: z.string(), value: z.string() }),
752
+ z.object({ icon: z.any(), value: z.string() })
753
+ ],
754
+ {
755
+ errorMap: () => {
756
+ return {
757
+ message: "Invalid option array. Must be a string[] or {label: string, value: string}[] or {icon: React.ComponentType<any>, value: string}[]"
758
+ };
759
+ }
705
760
  }
706
- });
761
+ );
707
762
  const TinaField = z.object({
708
763
  name,
709
764
  label: z.string().or(z.boolean()).optional(),
@@ -773,6 +828,7 @@ const TinaFieldZod = z.lazy(() => {
773
828
  }
774
829
  });
775
830
  const ObjectField = FieldWithList.extend({
831
+ // needs to be redefined here to avoid circle deps
776
832
  type: z.literal("object", {
777
833
  invalid_type_error: typeTypeError,
778
834
  required_error: typeRequiredError
@@ -811,35 +867,43 @@ const TinaFieldZod = z.lazy(() => {
811
867
  }
812
868
  })
813
869
  });
814
- return z.discriminatedUnion("type", [
815
- StringField,
816
- BooleanField,
817
- NumberField,
818
- ImageField,
819
- DateTimeField,
820
- ReferenceField,
821
- ObjectField,
822
- RichTextField
823
- ], {
824
- errorMap: (issue, ctx) => {
825
- var _a;
826
- if (issue.code === "invalid_union_discriminator") {
870
+ return z.discriminatedUnion(
871
+ "type",
872
+ [
873
+ StringField,
874
+ BooleanField,
875
+ NumberField,
876
+ ImageField,
877
+ DateTimeField,
878
+ ReferenceField,
879
+ ObjectField,
880
+ RichTextField
881
+ ],
882
+ {
883
+ errorMap: (issue, ctx) => {
884
+ var _a;
885
+ if (issue.code === "invalid_union_discriminator") {
886
+ return {
887
+ message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
888
+ };
889
+ }
827
890
  return {
828
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
891
+ message: issue.message
829
892
  };
830
893
  }
831
- return {
832
- message: issue.message
833
- };
834
894
  }
835
- }).superRefine((val, ctx) => {
895
+ ).superRefine((val, ctx) => {
836
896
  if (val.type === "string") {
837
897
  if (val.isTitle) {
838
898
  if (val.list) {
839
899
  ctx.addIssue({
840
900
  code: z.ZodIssueCode.custom,
841
901
  message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
842
- ${JSON.stringify(val, null, 2)}
902
+ ${JSON.stringify(
903
+ val,
904
+ null,
905
+ 2
906
+ )}
843
907
  `
844
908
  });
845
909
  }
@@ -847,7 +911,11 @@ ${JSON.stringify(val, null, 2)}
847
911
  ctx.addIssue({
848
912
  code: z.ZodIssueCode.custom,
849
913
  message: `Must have { required: true } when using \`isTitle\` Error in value
850
- ${JSON.stringify(val, null, 2)}
914
+ ${JSON.stringify(
915
+ val,
916
+ null,
917
+ 2
918
+ )}
851
919
  `
852
920
  });
853
921
  }
@@ -957,12 +1025,16 @@ const TinaCloudCollection = CollectionBaseSchema.extend({
957
1025
  message: `Fields must have a unique name, duplicate field names: ${dups}`
958
1026
  });
959
1027
  }
960
- }).refine((val) => {
961
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
962
- return arr.length < 2;
963
- }, {
964
- message: "Fields can only have one use of `isTitle`"
965
- }),
1028
+ }).refine(
1029
+ // It is valid if it is 0 or 1
1030
+ (val) => {
1031
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
1032
+ return arr.length < 2;
1033
+ },
1034
+ {
1035
+ message: "Fields can only have one use of `isTitle`"
1036
+ }
1037
+ ),
966
1038
  templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
967
1039
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
968
1040
  if (dups) {
@@ -972,15 +1044,18 @@ const TinaCloudCollection = CollectionBaseSchema.extend({
972
1044
  });
973
1045
  }
974
1046
  })
975
- }).refine((val) => {
976
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
977
- if (!isValid) {
978
- return false;
979
- } else {
980
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
981
- return isValid;
982
- }
983
- }, { message: "Must provide one of templates or fields in your collection" });
1047
+ }).refine(
1048
+ (val) => {
1049
+ let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
1050
+ if (!isValid) {
1051
+ return false;
1052
+ } else {
1053
+ isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
1054
+ return isValid;
1055
+ }
1056
+ },
1057
+ { message: "Must provide one of templates or fields in your collection" }
1058
+ );
984
1059
  const TinaCloudSchemaZod = z.object({
985
1060
  collections: z.array(TinaCloudCollection),
986
1061
  config: tinaConfigZod.optional()
@@ -1031,4 +1106,16 @@ const validateSchema = ({ schema }) => {
1031
1106
  }
1032
1107
  }
1033
1108
  };
1034
- export { NAMER, TINA_HOST, TinaSchema, TinaSchemaValidationError, addNamespaceToSchema, normalizePath, parseURL, resolveField, resolveForm, validateSchema, validateTinaCloudSchemaConfig };
1109
+ export {
1110
+ NAMER,
1111
+ TINA_HOST,
1112
+ TinaSchema,
1113
+ TinaSchemaValidationError,
1114
+ addNamespaceToSchema,
1115
+ normalizePath,
1116
+ parseURL,
1117
+ resolveField,
1118
+ resolveForm,
1119
+ validateSchema,
1120
+ validateTinaCloudSchemaConfig
1121
+ };
package/dist/index.js CHANGED
@@ -2,33 +2,23 @@
2
2
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("micromatch"), require("yup"), require("url-pattern"), require("zod")) : typeof define === "function" && define.amd ? define(["exports", "micromatch", "yup", "url-pattern", "zod"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["@tinacms/schema-tools"] = {}, global.NOOP, global.NOOP, global.NOOP, global.NOOP));
3
3
  })(this, function(exports2, micromatch, yup, UrlPattern, z) {
4
4
  "use strict";
5
- function _interopDefaultLegacy(e) {
6
- return e && typeof e === "object" && "default" in e ? e : { "default": e };
7
- }
8
- function _interopNamespace(e) {
9
- if (e && e.__esModule)
10
- return e;
11
- var n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
5
+ function _interopNamespaceDefault(e) {
6
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
12
7
  if (e) {
13
- Object.keys(e).forEach(function(k) {
8
+ for (const k in e) {
14
9
  if (k !== "default") {
15
- var d = Object.getOwnPropertyDescriptor(e, k);
10
+ const d = Object.getOwnPropertyDescriptor(e, k);
16
11
  Object.defineProperty(n, k, d.get ? d : {
17
12
  enumerable: true,
18
- get: function() {
19
- return e[k];
20
- }
13
+ get: () => e[k]
21
14
  });
22
15
  }
23
- });
16
+ }
24
17
  }
25
- n["default"] = e;
18
+ n.default = e;
26
19
  return Object.freeze(n);
27
20
  }
28
- var micromatch__default = /* @__PURE__ */ _interopDefaultLegacy(micromatch);
29
- var yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
30
- var UrlPattern__default = /* @__PURE__ */ _interopDefaultLegacy(UrlPattern);
31
- var z__default = /* @__PURE__ */ _interopDefaultLegacy(z);
21
+ const yup__namespace = /* @__PURE__ */ _interopNamespaceDefault(yup);
32
22
  function addNamespaceToSchema(maybeNode, namespace = []) {
33
23
  if (typeof maybeNode === "string") {
34
24
  return maybeNode;
@@ -171,15 +161,18 @@
171
161
  host: params.host
172
162
  };
173
163
  }
174
- const pattern = new UrlPattern__default["default"]("/:v/content/:clientId/github/*", {
164
+ const pattern = new UrlPattern("/:v/content/:clientId/github/*", {
175
165
  escapeChar: " ",
166
+ // extend to allow `.` in version
176
167
  segmentValueCharset: "a-zA-Z0-9-_~ %."
177
168
  });
178
169
  const result = pattern.match(params.pathname);
179
170
  const branch = result == null ? void 0 : result._;
180
171
  const clientId = result == null ? void 0 : result.clientId;
181
172
  if (!branch || !clientId) {
182
- throw new Error(`Invalid URL format provided. Expected: https://content.tinajs.io/<Version>/content/<ClientID>/github/<Branch> but but received ${url}`);
173
+ throw new Error(
174
+ `Invalid URL format provided. Expected: https://content.tinajs.io/<Version>/content/<ClientID>/github/<Branch> but but received ${url}`
175
+ );
183
176
  }
184
177
  return {
185
178
  host: params.host,
@@ -190,6 +183,9 @@
190
183
  };
191
184
  const normalizePath = (filepath) => filepath.replace(/\\/g, "/");
192
185
  class TinaSchema {
186
+ /**
187
+ * Create a schema class from a user defined schema object
188
+ */
193
189
  constructor(config) {
194
190
  this.config = config;
195
191
  this.getIsTitleFieldName = (collection) => {
@@ -199,10 +195,14 @@
199
195
  return field == null ? void 0 : field.name;
200
196
  };
201
197
  this.getCollectionsByName = (collectionNames) => {
202
- return this.schema.collections.filter((collection) => collectionNames.includes(collection.name));
198
+ return this.schema.collections.filter(
199
+ (collection) => collectionNames.includes(collection.name)
200
+ );
203
201
  };
204
202
  this.getCollection = (collectionName) => {
205
- const collection = this.schema.collections.find((collection2) => collection2.name === collectionName);
203
+ const collection = this.schema.collections.find(
204
+ (collection2) => collection2.name === collectionName
205
+ );
206
206
  if (!collection) {
207
207
  throw new Error(`Expected to find collection named ${collectionName}`);
208
208
  }
@@ -217,6 +217,7 @@
217
217
  break;
218
218
  }
219
219
  return {
220
+ // @ts-ignore FIXME: backwards compatibility, using `slug` should probably be deprecated
220
221
  slug: collection.name,
221
222
  ...extraFields,
222
223
  ...collection,
@@ -224,7 +225,9 @@
224
225
  };
225
226
  };
226
227
  this.getCollections = () => {
227
- return this.schema.collections.map((collection) => this.getCollection(collection.name)) || [];
228
+ return this.schema.collections.map(
229
+ (collection) => this.getCollection(collection.name)
230
+ ) || [];
228
231
  };
229
232
  this.getCollectionByFullPath = (filepath) => {
230
233
  const fileExtension = filepath.split(".").pop();
@@ -235,7 +238,7 @@
235
238
  }
236
239
  if (((_a = collection == null ? void 0 : collection.match) == null ? void 0 : _a.include) || ((_b = collection == null ? void 0 : collection.match) == null ? void 0 : _b.exclude)) {
237
240
  const matches = this.getMatches({ collection });
238
- const match = micromatch__default["default"]([filepath], matches).length > 0;
241
+ const match = micromatch([filepath], matches).length > 0;
239
242
  if (!match) {
240
243
  return false;
241
244
  }
@@ -265,19 +268,27 @@
265
268
  const templates = this.getTemplatesForCollectable(collection);
266
269
  if (templates.type === "union") {
267
270
  if (templateName) {
268
- template = templates.templates.find((template2) => lastItem(template2.namespace) === templateName);
271
+ template = templates.templates.find(
272
+ (template2) => lastItem(template2.namespace) === templateName
273
+ );
269
274
  if (!template) {
270
- throw new Error(`Unable to determine template for item at ${filepath}`);
275
+ throw new Error(
276
+ `Unable to determine template for item at ${filepath}`
277
+ );
271
278
  }
272
279
  } else {
273
- throw new Error(`Unable to determine template for item at ${filepath}, no template name provided for collection with multiple templates`);
280
+ throw new Error(
281
+ `Unable to determine template for item at ${filepath}, no template name provided for collection with multiple templates`
282
+ );
274
283
  }
275
284
  }
276
285
  if (templates.type === "object") {
277
286
  template = templates.template;
278
287
  }
279
288
  if (!template) {
280
- throw new Error(`Something went wrong while trying to determine template for ${filepath}`);
289
+ throw new Error(
290
+ `Something went wrong while trying to determine template for ${filepath}`
291
+ );
281
292
  }
282
293
  return { collection, template };
283
294
  };
@@ -290,10 +301,17 @@
290
301
  case "object":
291
302
  return templateInfo.template;
292
303
  case "union":
293
- assertShape(data, (yup2) => yup2.object({ _template: yup2.string().required() }));
294
- const template = templateInfo.templates.find((template2) => template2.namespace[template2.namespace.length - 1] === data._template);
304
+ assertShape(
305
+ data,
306
+ (yup2) => yup2.object({ _template: yup2.string().required() })
307
+ );
308
+ const template = templateInfo.templates.find(
309
+ (template2) => template2.namespace[template2.namespace.length - 1] === data._template
310
+ );
295
311
  if (!template) {
296
- throw new Error(`Expected to find template named '${data._template}' for collection '${lastItem(collection.namespace)}'`);
312
+ throw new Error(
313
+ `Expected to find template named '${data._template}' for collection '${lastItem(collection.namespace)}'`
314
+ );
297
315
  }
298
316
  return template;
299
317
  }
@@ -347,7 +365,10 @@
347
365
  if (field.type === "object")
348
366
  if (field.templates) {
349
367
  if (field.list) {
350
- assertShape(value, (yup2) => yup2.array(yup2.object({ _template: yup2.string().required() })));
368
+ assertShape(
369
+ value,
370
+ (yup2) => yup2.array(yup2.object({ _template: yup2.string().required() }))
371
+ );
351
372
  return value.map((item) => {
352
373
  const { _template, ...rest } = item;
353
374
  const template = field.templates.find((template2) => {
@@ -364,7 +385,10 @@
364
385
  };
365
386
  });
366
387
  } else {
367
- assertShape(value, (yup2) => yup2.object({ _template: yup2.string().required() }));
388
+ assertShape(
389
+ value,
390
+ (yup2) => yup2.object({ _template: yup2.string().required() })
391
+ );
368
392
  const { _template, ...rest } = value;
369
393
  return { [_template]: this.transformCollectablePayload(rest, field) };
370
394
  }
@@ -395,7 +419,7 @@
395
419
  return false;
396
420
  };
397
421
  this.getTemplatesForCollectable = (collection) => {
398
- let extraFields = [];
422
+ const extraFields = [];
399
423
  if (collection == null ? void 0 : collection.fields) {
400
424
  const template = collection;
401
425
  if (typeof template.fields === "string" || typeof template.fields === "undefined") {
@@ -423,7 +447,11 @@
423
447
  })
424
448
  };
425
449
  } else {
426
- throw new Error(`Expected either fields or templates array to be defined on collection ${collection.namespace.join("_")}`);
450
+ throw new Error(
451
+ `Expected either fields or templates array to be defined on collection ${collection.namespace.join(
452
+ "_"
453
+ )}`
454
+ );
427
455
  }
428
456
  }
429
457
  };
@@ -469,6 +497,12 @@
469
497
  }
470
498
  });
471
499
  }
500
+ /**
501
+ * This function returns an array of glob matches for a given collection.
502
+ *
503
+ * @param collection The collection to get the matches for. Can be a string or a collection object.
504
+ * @returns An array of glob matches.
505
+ */
472
506
  getMatches({
473
507
  collection: collectionOrString
474
508
  }) {
@@ -493,7 +527,7 @@
493
527
  files
494
528
  }) {
495
529
  const matches = this.getMatches({ collection });
496
- const matchedFiles = micromatch__default["default"](files, matches);
530
+ const matchedFiles = micromatch(files, matches);
497
531
  return matchedFiles;
498
532
  }
499
533
  }
@@ -563,6 +597,7 @@
563
597
  }
564
598
  if (field.list) {
565
599
  return {
600
+ // Allows component to be overridden for scalars
566
601
  component: "list",
567
602
  field: {
568
603
  component: "text"
@@ -572,6 +607,7 @@
572
607
  };
573
608
  }
574
609
  return {
610
+ // Allows component to be overridden for scalars
575
611
  component: "text",
576
612
  ...field,
577
613
  ...extraFields
@@ -582,7 +618,9 @@
582
618
  return {
583
619
  ...field,
584
620
  component: field.list ? "group-list" : "group",
585
- fields: templateInfo.template.fields.map((field2) => resolveField(field2, schema)),
621
+ fields: templateInfo.template.fields.map(
622
+ (field2) => resolveField(field2, schema)
623
+ ),
586
624
  ...extraFields
587
625
  };
588
626
  } else if (templateInfo.type === "union") {
@@ -673,7 +711,9 @@
673
711
  moreInfo.push(parseZodError({ zodError: unionError }));
674
712
  });
675
713
  }
676
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(".")}`;
714
+ const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
715
+ "."
716
+ )}`;
677
717
  const errorMessages = [errorMessage, ...moreInfo];
678
718
  return {
679
719
  errors: errorMessages
@@ -719,18 +759,23 @@ If you need to use this value in your content you can use the \`nameOverride\` p
719
759
  "rich-text"
720
760
  ];
721
761
  const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
722
- const typeRequiredError = `type is required and must be one of ${TypeName.join(", ")}`;
723
- const Option = z.z.union([
724
- z.z.string(),
725
- z.z.object({ label: z.z.string(), value: z.z.string() }),
726
- z.z.object({ icon: z.z.any(), value: z.z.string() })
727
- ], {
728
- errorMap: () => {
729
- return {
730
- message: "Invalid option array. Must be a string[] or {label: string, value: string}[] or {icon: React.ComponentType<any>, value: string}[]"
731
- };
762
+ const typeRequiredError = `type is required and must be one of ${TypeName.join(
763
+ ", "
764
+ )}`;
765
+ const Option = z.z.union(
766
+ [
767
+ z.z.string(),
768
+ z.z.object({ label: z.z.string(), value: z.z.string() }),
769
+ z.z.object({ icon: z.z.any(), value: z.z.string() })
770
+ ],
771
+ {
772
+ errorMap: () => {
773
+ return {
774
+ message: "Invalid option array. Must be a string[] or {label: string, value: string}[] or {icon: React.ComponentType<any>, value: string}[]"
775
+ };
776
+ }
732
777
  }
733
- });
778
+ );
734
779
  const TinaField = z.z.object({
735
780
  name,
736
781
  label: z.z.string().or(z.z.boolean()).optional(),
@@ -800,6 +845,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
800
845
  }
801
846
  });
802
847
  const ObjectField = FieldWithList.extend({
848
+ // needs to be redefined here to avoid circle deps
803
849
  type: z.z.literal("object", {
804
850
  invalid_type_error: typeTypeError,
805
851
  required_error: typeRequiredError
@@ -838,35 +884,43 @@ If you need to use this value in your content you can use the \`nameOverride\` p
838
884
  }
839
885
  })
840
886
  });
841
- return z.z.discriminatedUnion("type", [
842
- StringField,
843
- BooleanField,
844
- NumberField,
845
- ImageField,
846
- DateTimeField,
847
- ReferenceField,
848
- ObjectField,
849
- RichTextField
850
- ], {
851
- errorMap: (issue, ctx) => {
852
- var _a;
853
- if (issue.code === "invalid_union_discriminator") {
887
+ return z.z.discriminatedUnion(
888
+ "type",
889
+ [
890
+ StringField,
891
+ BooleanField,
892
+ NumberField,
893
+ ImageField,
894
+ DateTimeField,
895
+ ReferenceField,
896
+ ObjectField,
897
+ RichTextField
898
+ ],
899
+ {
900
+ errorMap: (issue, ctx) => {
901
+ var _a;
902
+ if (issue.code === "invalid_union_discriminator") {
903
+ return {
904
+ message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
905
+ };
906
+ }
854
907
  return {
855
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
908
+ message: issue.message
856
909
  };
857
910
  }
858
- return {
859
- message: issue.message
860
- };
861
911
  }
862
- }).superRefine((val, ctx) => {
912
+ ).superRefine((val, ctx) => {
863
913
  if (val.type === "string") {
864
914
  if (val.isTitle) {
865
915
  if (val.list) {
866
916
  ctx.addIssue({
867
917
  code: z.z.ZodIssueCode.custom,
868
918
  message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
869
- ${JSON.stringify(val, null, 2)}
919
+ ${JSON.stringify(
920
+ val,
921
+ null,
922
+ 2
923
+ )}
870
924
  `
871
925
  });
872
926
  }
@@ -874,7 +928,11 @@ ${JSON.stringify(val, null, 2)}
874
928
  ctx.addIssue({
875
929
  code: z.z.ZodIssueCode.custom,
876
930
  message: `Must have { required: true } when using \`isTitle\` Error in value
877
- ${JSON.stringify(val, null, 2)}
931
+ ${JSON.stringify(
932
+ val,
933
+ null,
934
+ 2
935
+ )}
878
936
  `
879
937
  });
880
938
  }
@@ -903,26 +961,26 @@ ${JSON.stringify(val, null, 2)}
903
961
  return true;
904
962
  });
905
963
  });
906
- const tinaConfigKey = z__default["default"].object({
907
- publicFolder: z__default["default"].string(),
908
- mediaRoot: z__default["default"].string()
964
+ const tinaConfigKey = z.object({
965
+ publicFolder: z.string(),
966
+ mediaRoot: z.string()
909
967
  }).strict().optional();
910
- const tinaSearchKey = z__default["default"].object({
911
- indexerToken: z__default["default"].string().optional(),
912
- stopwordLanguages: z__default["default"].array(z__default["default"].string()).nonempty().optional(),
913
- tokenSplitRegex: z__default["default"].string().optional()
968
+ const tinaSearchKey = z.object({
969
+ indexerToken: z.string().optional(),
970
+ stopwordLanguages: z.array(z.string()).nonempty().optional(),
971
+ tokenSplitRegex: z.string().optional()
914
972
  }).strict().optional();
915
- const tinaConfigZod = z__default["default"].object({
916
- client: z__default["default"].object({ referenceDepth: z__default["default"].number().optional() }).optional(),
917
- media: z__default["default"].object({
973
+ const tinaConfigZod = z.object({
974
+ client: z.object({ referenceDepth: z.number().optional() }).optional(),
975
+ media: z.object({
918
976
  tina: tinaConfigKey,
919
- loadCustomStore: z__default["default"].function().optional()
977
+ loadCustomStore: z.function().optional()
920
978
  }).optional(),
921
- search: z__default["default"].object({
979
+ search: z.object({
922
980
  tina: tinaSearchKey,
923
- searchClient: z__default["default"].any().optional(),
924
- indexBatchSize: z__default["default"].number().gte(1).optional(),
925
- maxSearchIndexFieldLength: z__default["default"].number().gte(1).optional()
981
+ searchClient: z.any().optional(),
982
+ indexBatchSize: z.number().gte(1).optional(),
983
+ maxSearchIndexFieldLength: z.number().gte(1).optional()
926
984
  }).optional()
927
985
  });
928
986
  const validateTinaCloudSchemaConfig = (config) => {
@@ -984,12 +1042,16 @@ ${JSON.stringify(val, null, 2)}
984
1042
  message: `Fields must have a unique name, duplicate field names: ${dups}`
985
1043
  });
986
1044
  }
987
- }).refine((val) => {
988
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
989
- return arr.length < 2;
990
- }, {
991
- message: "Fields can only have one use of `isTitle`"
992
- }),
1045
+ }).refine(
1046
+ // It is valid if it is 0 or 1
1047
+ (val) => {
1048
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
1049
+ return arr.length < 2;
1050
+ },
1051
+ {
1052
+ message: "Fields can only have one use of `isTitle`"
1053
+ }
1054
+ ),
993
1055
  templates: z.z.array(Template).min(1).optional().superRefine((val, ctx) => {
994
1056
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
995
1057
  if (dups) {
@@ -999,15 +1061,18 @@ ${JSON.stringify(val, null, 2)}
999
1061
  });
1000
1062
  }
1001
1063
  })
1002
- }).refine((val) => {
1003
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
1004
- if (!isValid) {
1005
- return false;
1006
- } else {
1007
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
1008
- return isValid;
1009
- }
1010
- }, { message: "Must provide one of templates or fields in your collection" });
1064
+ }).refine(
1065
+ (val) => {
1066
+ let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
1067
+ if (!isValid) {
1068
+ return false;
1069
+ } else {
1070
+ isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
1071
+ return isValid;
1072
+ }
1073
+ },
1074
+ { message: "Must provide one of templates or fields in your collection" }
1075
+ );
1011
1076
  const TinaCloudSchemaZod = z.z.object({
1012
1077
  collections: z.z.array(TinaCloudCollection),
1013
1078
  config: tinaConfigZod.optional()
@@ -1069,5 +1134,5 @@ ${JSON.stringify(val, null, 2)}
1069
1134
  exports2.resolveForm = resolveForm;
1070
1135
  exports2.validateSchema = validateSchema;
1071
1136
  exports2.validateTinaCloudSchemaConfig = validateTinaCloudSchemaConfig;
1072
- Object.defineProperties(exports2, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
1137
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
1073
1138
  });
@@ -335,17 +335,45 @@ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, Do
335
335
  */
336
336
  schema: Schema;
337
337
  /**
338
- * The base branch to pull content from. Note that this is ignored for local development
338
+ * The base branch to pull content from.
339
+ *
340
+ * Note: This is ignored and not needed for local development or when self-hosting
339
341
  */
340
- branch: string | null;
342
+ branch?: string | null;
341
343
  /**
342
344
  * Your clientId from app.tina.io
345
+ *
346
+ * Note: This is ignored and not needed for local development or when self-hosting
343
347
  */
344
- clientId: string | null;
348
+ clientId?: string | null;
345
349
  /**
346
350
  * Your read only token from app.tina.io
351
+ *
352
+ * Note: This is ignored and not needed for local development or when self-hosting
347
353
  */
348
- token: string | null;
354
+ token?: string | null;
355
+ ui?: {
356
+ /**
357
+ * When using Tina Cloud's branching feature, provide the URL for your given branch
358
+ *
359
+ * Eg. If you're deplying to Vercel, and your repo name is 'my-app',
360
+ * Vercel's preview URL would be based on the branch:
361
+ *
362
+ * ```js
363
+ * previewUrl: (context) => {
364
+ * const repoName = 'my-app'
365
+ * // `https://<project-name>-git-<branch-name>.vercel.app`
366
+ * return { url: `https://my-app-git-${context.branch}` }
367
+ * }
368
+ * ```
369
+ * [more info](https://vercel.com/docs/concepts/deployments/generated-urls#url-with-git-branch)
370
+ */
371
+ previewUrl: (context: {
372
+ branch: string;
373
+ }) => {
374
+ url: string;
375
+ };
376
+ };
349
377
  /**
350
378
  * Configurations for the autogenerated GraphQL HTTP client
351
379
  */
@@ -3,4 +3,4 @@
3
3
  */
4
4
  import * as yup from 'yup';
5
5
  import type { AnySchema } from 'yup';
6
- export declare function assertShape<T extends unknown>(value: unknown, yupSchema: (args: typeof yup) => AnySchema, errorMessage?: string): asserts value is T;
6
+ export declare function assertShape<T>(value: unknown, yupSchema: (args: typeof yup) => AnySchema, errorMessage?: string): asserts value is T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/schema-tools",
3
- "version": "1.4.7",
3
+ "version": "1.4.8",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.es.js",
6
6
  "exports": {
@@ -26,7 +26,7 @@
26
26
  "@tinacms/scripts": "1.1.0",
27
27
  "@types/micromatch": "^4.0.2",
28
28
  "@types/yup": "^0.29.10",
29
- "jest": "^27.0.6",
29
+ "jest": "^29.5.0",
30
30
  "react": "17.0.2",
31
31
  "typescript": "4.3.5",
32
32
  "yup": "^0.32.0"