@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 +151 -64
- package/dist/index.js +165 -100
- package/dist/types/index.d.ts +32 -4
- package/dist/util/validate.d.ts +1 -1
- package/package.json +2 -2
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
254
|
+
template = templates.templates.find(
|
|
255
|
+
(template2) => lastItem(template2.namespace) === templateName
|
|
256
|
+
);
|
|
242
257
|
if (!template) {
|
|
243
|
-
throw new Error(
|
|
258
|
+
throw new Error(
|
|
259
|
+
`Unable to determine template for item at ${filepath}`
|
|
260
|
+
);
|
|
244
261
|
}
|
|
245
262
|
} else {
|
|
246
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
267
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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(
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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:
|
|
891
|
+
message: issue.message
|
|
829
892
|
};
|
|
830
893
|
}
|
|
831
|
-
return {
|
|
832
|
-
message: issue.message
|
|
833
|
-
};
|
|
834
894
|
}
|
|
835
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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(
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
}
|
|
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 {
|
|
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
|
|
6
|
-
|
|
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
|
-
|
|
8
|
+
for (const k in e) {
|
|
14
9
|
if (k !== "default") {
|
|
15
|
-
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
11
|
Object.defineProperty(n, k, d.get ? d : {
|
|
17
12
|
enumerable: true,
|
|
18
|
-
get:
|
|
19
|
-
return e[k];
|
|
20
|
-
}
|
|
13
|
+
get: () => e[k]
|
|
21
14
|
});
|
|
22
15
|
}
|
|
23
|
-
}
|
|
16
|
+
}
|
|
24
17
|
}
|
|
25
|
-
n
|
|
18
|
+
n.default = e;
|
|
26
19
|
return Object.freeze(n);
|
|
27
20
|
}
|
|
28
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
271
|
+
template = templates.templates.find(
|
|
272
|
+
(template2) => lastItem(template2.namespace) === templateName
|
|
273
|
+
);
|
|
269
274
|
if (!template) {
|
|
270
|
-
throw new Error(
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Unable to determine template for item at ${filepath}`
|
|
277
|
+
);
|
|
271
278
|
}
|
|
272
279
|
} else {
|
|
273
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
294
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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:
|
|
908
|
+
message: issue.message
|
|
856
909
|
};
|
|
857
910
|
}
|
|
858
|
-
return {
|
|
859
|
-
message: issue.message
|
|
860
|
-
};
|
|
861
911
|
}
|
|
862
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
907
|
-
publicFolder:
|
|
908
|
-
mediaRoot:
|
|
964
|
+
const tinaConfigKey = z.object({
|
|
965
|
+
publicFolder: z.string(),
|
|
966
|
+
mediaRoot: z.string()
|
|
909
967
|
}).strict().optional();
|
|
910
|
-
const tinaSearchKey =
|
|
911
|
-
indexerToken:
|
|
912
|
-
stopwordLanguages:
|
|
913
|
-
tokenSplitRegex:
|
|
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 =
|
|
916
|
-
client:
|
|
917
|
-
media:
|
|
973
|
+
const tinaConfigZod = z.object({
|
|
974
|
+
client: z.object({ referenceDepth: z.number().optional() }).optional(),
|
|
975
|
+
media: z.object({
|
|
918
976
|
tina: tinaConfigKey,
|
|
919
|
-
loadCustomStore:
|
|
977
|
+
loadCustomStore: z.function().optional()
|
|
920
978
|
}).optional(),
|
|
921
|
-
search:
|
|
979
|
+
search: z.object({
|
|
922
980
|
tina: tinaSearchKey,
|
|
923
|
-
searchClient:
|
|
924
|
-
indexBatchSize:
|
|
925
|
-
maxSearchIndexFieldLength:
|
|
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(
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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(
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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.
|
|
1137
|
+
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
1073
1138
|
});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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
|
*/
|
package/dist/util/validate.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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": "^
|
|
29
|
+
"jest": "^29.5.0",
|
|
30
30
|
"react": "17.0.2",
|
|
31
31
|
"typescript": "4.3.5",
|
|
32
32
|
"yup": "^0.32.0"
|