@tinacms/schema-tools 1.6.7 → 1.6.9

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.js CHANGED
@@ -2004,6 +2004,20 @@
2004
2004
  field.uid = field.uid || false;
2005
2005
  });
2006
2006
  }
2007
+ findReferences(name2) {
2008
+ const result = {};
2009
+ this.walkFields(({ field, collection: c, path }) => {
2010
+ if (field.type === "reference") {
2011
+ if (field.collections.includes(name2)) {
2012
+ if (result[c.name] === void 0) {
2013
+ result[c.name] = [];
2014
+ }
2015
+ result[c.name].push({ path, field });
2016
+ }
2017
+ }
2018
+ });
2019
+ return result;
2020
+ }
2007
2021
  /**
2008
2022
  * This function returns an array of glob matches for a given collection.
2009
2023
  *
@@ -2226,9 +2240,12 @@
2226
2240
  moreInfo.push(parseZodError({ zodError: unionError }));
2227
2241
  });
2228
2242
  }
2229
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2243
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2244
+ Additional information:
2245
+ - Error found at path ${issue.path.join(
2230
2246
  "."
2231
- )}`;
2247
+ )}
2248
+ `;
2232
2249
  const errorMessages = [errorMessage, ...moreInfo];
2233
2250
  return {
2234
2251
  errors: errorMessages
@@ -2263,6 +2280,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2263
2280
  });
2264
2281
  }
2265
2282
  });
2283
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2284
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2285
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2266
2286
  const TypeName = [
2267
2287
  "string",
2268
2288
  "boolean",
@@ -2273,10 +2293,11 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2273
2293
  "reference",
2274
2294
  "rich-text"
2275
2295
  ];
2276
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2277
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2278
- ", "
2279
- )}`;
2296
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2297
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2298
+ ${formattedTypes}`;
2299
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2300
+ ${formattedTypes}`;
2280
2301
  const Option = z.z.union(
2281
2302
  [
2282
2303
  z.z.string(),
@@ -2362,7 +2383,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2362
2383
  if (dups) {
2363
2384
  ctx.addIssue({
2364
2385
  code: z.z.ZodIssueCode.custom,
2365
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2386
+ message: duplicateFieldErrorMessage(dups)
2366
2387
  });
2367
2388
  }
2368
2389
  });
@@ -2372,21 +2393,21 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2372
2393
  invalid_type_error: typeTypeError,
2373
2394
  required_error: typeRequiredError
2374
2395
  }),
2375
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2396
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2376
2397
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2377
2398
  if (dups) {
2378
2399
  ctx.addIssue({
2379
2400
  code: z.z.ZodIssueCode.custom,
2380
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2401
+ message: duplicateFieldErrorMessage(dups)
2381
2402
  });
2382
2403
  }
2383
2404
  }),
2384
- templates: z.z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2405
+ templates: z.z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2385
2406
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2386
2407
  if (dups) {
2387
2408
  ctx.addIssue({
2388
2409
  code: z.z.ZodIssueCode.custom,
2389
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2410
+ message: duplicateTemplateErrorMessage(dups)
2390
2411
  });
2391
2412
  }
2392
2413
  })
@@ -2401,7 +2422,7 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2401
2422
  if (dups) {
2402
2423
  ctx.addIssue({
2403
2424
  code: z.z.ZodIssueCode.custom,
2404
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2425
+ message: duplicateTemplateErrorMessage(dups)
2405
2426
  });
2406
2427
  }
2407
2428
  })
@@ -2421,10 +2442,17 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2421
2442
  ],
2422
2443
  {
2423
2444
  errorMap: (issue, ctx) => {
2424
- var _a;
2445
+ var _a, _b;
2425
2446
  if (issue.code === "invalid_union_discriminator") {
2447
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2448
+ return {
2449
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2450
+ ${formattedTypes}`
2451
+ };
2452
+ }
2426
2453
  return {
2427
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
2454
+ message: `Invalid \`type\` property in field \`${ctx.data.name}\`. In the schema is 'type: ${(_b = ctx.data) == null ? void 0 : _b.type}' but expected one of the following:
2455
+ ${formattedTypes}`
2428
2456
  };
2429
2457
  }
2430
2458
  return {
@@ -2434,77 +2462,52 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2434
2462
  }
2435
2463
  ).superRefine((val, ctx) => {
2436
2464
  if (val.type === "string") {
2437
- if (val.isTitle) {
2438
- if (val.list) {
2439
- ctx.addIssue({
2440
- code: z.z.ZodIssueCode.custom,
2441
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2442
- ${JSON.stringify(
2443
- val,
2444
- null,
2445
- 2
2446
- )}
2447
- `
2448
- });
2449
- }
2450
- if (!val.required) {
2451
- ctx.addIssue({
2452
- code: z.z.ZodIssueCode.custom,
2453
- message: `Must have { required: true } when using \`isTitle\` Error in value
2454
- ${JSON.stringify(
2455
- val,
2456
- null,
2457
- 2
2458
- )}
2459
- `
2460
- });
2461
- }
2465
+ const stringifiedField = JSON.stringify(val, null, 2);
2466
+ if (val.isTitle && val.list) {
2467
+ ctx.addIssue({
2468
+ code: z.z.ZodIssueCode.custom,
2469
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2470
+ ${stringifiedField}`
2471
+ });
2462
2472
  }
2463
- if (val.uid) {
2473
+ if (val.isTitle && !val.required) {
2474
+ ctx.addIssue({
2475
+ code: z.z.ZodIssueCode.custom,
2476
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2477
+ ${stringifiedField}`
2478
+ });
2479
+ }
2480
+ if (val.uid && val.list) {
2464
2481
  if (val.list) {
2465
2482
  ctx.addIssue({
2466
2483
  code: z.z.ZodIssueCode.custom,
2467
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2468
- ${JSON.stringify(
2469
- val,
2470
- null,
2471
- 2
2472
- )}
2473
- `
2474
- });
2475
- }
2476
- if (!val.required) {
2477
- ctx.addIssue({
2478
- code: z.z.ZodIssueCode.custom,
2479
- message: `Must have { required: true } when using \`uid\` Error in value
2480
- ${JSON.stringify(
2481
- val,
2482
- null,
2483
- 2
2484
- )}
2485
- `
2484
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2485
+ ${stringifiedField}`
2486
2486
  });
2487
2487
  }
2488
2488
  }
2489
+ if (val.uid && !val.required) {
2490
+ ctx.addIssue({
2491
+ code: z.z.ZodIssueCode.custom,
2492
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2493
+ ${stringifiedField}`
2494
+ });
2495
+ }
2489
2496
  }
2490
2497
  if (val.type === "object") {
2491
- const message = "Must provide one of templates or fields in your collection";
2492
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2493
- if (!isValid) {
2498
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2494
2499
  ctx.addIssue({
2495
2500
  code: z.z.ZodIssueCode.custom,
2496
- message
2501
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2502
+ });
2503
+ return false;
2504
+ }
2505
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2506
+ ctx.addIssue({
2507
+ code: z.z.ZodIssueCode.custom,
2508
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2497
2509
  });
2498
2510
  return false;
2499
- } else {
2500
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2501
- if (!isValid) {
2502
- ctx.addIssue({
2503
- code: z.z.ZodIssueCode.custom,
2504
- message
2505
- });
2506
- }
2507
- return isValid;
2508
2511
  }
2509
2512
  }
2510
2513
  return true;
@@ -2548,8 +2551,8 @@ ${JSON.stringify(
2548
2551
  ];
2549
2552
  const Template = z.z.object({
2550
2553
  label: z.z.string({
2551
- invalid_type_error: "label must be a string",
2552
- required_error: "label was not provided but is required"
2554
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2555
+ required_error: "Missing `label` property. Property `label` is required."
2553
2556
  }),
2554
2557
  name,
2555
2558
  fields: z.z.array(TinaFieldZod)
@@ -2559,7 +2562,7 @@ ${JSON.stringify(
2559
2562
  if (dups) {
2560
2563
  ctx.addIssue({
2561
2564
  code: z.z.ZodIssueCode.custom,
2562
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2565
+ message: duplicateFieldErrorMessage(dups)
2563
2566
  });
2564
2567
  }
2565
2568
  });
@@ -2569,7 +2572,7 @@ ${JSON.stringify(
2569
2572
  if (val === "relativePath") {
2570
2573
  ctx.addIssue({
2571
2574
  code: z.z.ZodIssueCode.custom,
2572
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2575
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2573
2576
  });
2574
2577
  }
2575
2578
  }),
@@ -2577,7 +2580,7 @@ ${JSON.stringify(
2577
2580
  if (val === ".") {
2578
2581
  ctx.addIssue({
2579
2582
  code: z.z.ZodIssueCode.custom,
2580
- message: `path cannot be '.'. Please use '/' or '' instead. `
2583
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2581
2584
  });
2582
2585
  }
2583
2586
  }),
@@ -2586,63 +2589,64 @@ ${JSON.stringify(
2586
2589
  isDetached: z.z.boolean().optional()
2587
2590
  });
2588
2591
  const TinaCloudCollection = CollectionBaseSchema.extend({
2589
- fields: z.z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2592
+ fields: z.z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2590
2593
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2591
2594
  if (dups) {
2592
2595
  ctx.addIssue({
2593
2596
  code: z.z.ZodIssueCode.custom,
2594
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2597
+ message: duplicateFieldErrorMessage(dups)
2595
2598
  });
2596
2599
  }
2597
- }).refine(
2598
- // It is valid if it is 0 or 1
2599
- (val) => {
2600
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2601
- return arr.length < 2;
2602
- },
2603
- {
2604
- message: "Fields can only have one use of `isTitle`"
2605
- }
2606
- ).refine(
2607
- // It is valid if it is 0 or 1
2608
- (val) => {
2609
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2610
- return arr.length < 2;
2611
- },
2612
- {
2613
- message: "Fields can only have one use of `uid`"
2614
- }
2615
- ).refine(
2616
- // It is valid if it is 0 or 1
2617
- (val) => {
2618
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2619
- return arr.length < 2;
2620
- },
2621
- {
2622
- message: "Fields can only have one use of `password` type"
2600
+ }).superRefine((val, ctx) => {
2601
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2602
+ if (arr.length > 1) {
2603
+ ctx.addIssue({
2604
+ code: z.z.ZodIssueCode.custom,
2605
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2606
+ });
2607
+ }
2608
+ }).superRefine((val, ctx) => {
2609
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2610
+ if (arr.length > 2) {
2611
+ ctx.addIssue({
2612
+ code: z.z.ZodIssueCode.custom,
2613
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2614
+ });
2615
+ }
2616
+ }).superRefine((val, ctx) => {
2617
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2618
+ if (arr.length > 2) {
2619
+ ctx.addIssue({
2620
+ code: z.z.ZodIssueCode.custom,
2621
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2622
+ });
2623
2623
  }
2624
- ),
2625
- templates: z.z.array(Template).min(1).optional().superRefine((val, ctx) => {
2624
+ }),
2625
+ templates: z.z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2626
2626
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2627
2627
  if (dups) {
2628
2628
  ctx.addIssue({
2629
2629
  code: z.z.ZodIssueCode.custom,
2630
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2630
+ message: duplicateFieldErrorMessage(dups)
2631
2631
  });
2632
2632
  }
2633
2633
  })
2634
- }).refine(
2635
- (val) => {
2636
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2637
- if (!isValid) {
2638
- return false;
2639
- } else {
2640
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2641
- return isValid;
2642
- }
2643
- },
2644
- { message: "Must provide one of templates or fields in your collection" }
2645
- );
2634
+ }).superRefine((val, ctx) => {
2635
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2636
+ ctx.addIssue({
2637
+ code: z.z.ZodIssueCode.custom,
2638
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2639
+ });
2640
+ return false;
2641
+ }
2642
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2643
+ ctx.addIssue({
2644
+ code: z.z.ZodIssueCode.custom,
2645
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2646
+ });
2647
+ return false;
2648
+ }
2649
+ });
2646
2650
  const TinaCloudSchemaZod = z.z.object({
2647
2651
  collections: z.z.array(TinaCloudCollection),
2648
2652
  config: tinaConfigZod.optional()
@@ -2652,14 +2656,14 @@ ${JSON.stringify(
2652
2656
  if (dups) {
2653
2657
  ctx.addIssue({
2654
2658
  code: z.z.ZodIssueCode.custom,
2655
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2659
+ message: duplicateCollectionErrorMessage(dups),
2656
2660
  fatal: true
2657
2661
  });
2658
2662
  }
2659
2663
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2660
2664
  ctx.addIssue({
2661
2665
  code: z.z.ZodIssueCode.custom,
2662
- message: `Only one collection can be marked as isAuthCollection`,
2666
+ message: "Only one collection can be marked as `isAuthCollection`.",
2663
2667
  fatal: true
2664
2668
  });
2665
2669
  }
@@ -2667,7 +2671,7 @@ ${JSON.stringify(
2667
2671
  if (media && media.tina && media.loadCustomStore) {
2668
2672
  ctx.addIssue({
2669
2673
  code: z.z.ZodIssueCode.custom,
2670
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2674
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2671
2675
  fatal: true,
2672
2676
  path: ["config", "media"]
2673
2677
  });
@@ -2676,7 +2680,7 @@ ${JSON.stringify(
2676
2680
  if (search && search.tina && search.searchClient) {
2677
2681
  ctx.addIssue({
2678
2682
  code: z.z.ZodIssueCode.custom,
2679
- message: "can not have both searchClient and tina. Must use one or the other",
2683
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2680
2684
  fatal: true,
2681
2685
  path: ["config", "search"]
2682
2686
  });
@@ -2694,7 +2698,7 @@ ${JSON.stringify(
2694
2698
  } catch (e) {
2695
2699
  if (e instanceof z.ZodError) {
2696
2700
  const errors = parseZodError({ zodError: e });
2697
- throw new TinaSchemaValidationError(errors.join(", \n"));
2701
+ throw new TinaSchemaValidationError(errors.join("\n"));
2698
2702
  }
2699
2703
  throw new Error(e);
2700
2704
  }
package/dist/index.mjs CHANGED
@@ -1986,6 +1986,20 @@ class TinaSchema {
1986
1986
  field.uid = field.uid || false;
1987
1987
  });
1988
1988
  }
1989
+ findReferences(name2) {
1990
+ const result = {};
1991
+ this.walkFields(({ field, collection: c, path }) => {
1992
+ if (field.type === "reference") {
1993
+ if (field.collections.includes(name2)) {
1994
+ if (result[c.name] === void 0) {
1995
+ result[c.name] = [];
1996
+ }
1997
+ result[c.name].push({ path, field });
1998
+ }
1999
+ }
2000
+ });
2001
+ return result;
2002
+ }
1989
2003
  /**
1990
2004
  * This function returns an array of glob matches for a given collection.
1991
2005
  *
@@ -2208,9 +2222,12 @@ const parseZodError = ({ zodError }) => {
2208
2222
  moreInfo.push(parseZodError({ zodError: unionError }));
2209
2223
  });
2210
2224
  }
2211
- const errorMessage = `Error ${issue == null ? void 0 : issue.message} at path ${issue.path.join(
2225
+ const errorMessage = `${issue == null ? void 0 : issue.message}
2226
+ Additional information:
2227
+ - Error found at path ${issue.path.join(
2212
2228
  "."
2213
- )}`;
2229
+ )}
2230
+ `;
2214
2231
  const errorMessages = [errorMessage, ...moreInfo];
2215
2232
  return {
2216
2233
  errors: errorMessages
@@ -2245,6 +2262,9 @@ If you need to use this value in your content you can use the \`nameOverride\` p
2245
2262
  });
2246
2263
  }
2247
2264
  });
2265
+ const duplicateFieldErrorMessage = (fields) => `Fields must have unique names. Found duplicate field names: [${fields}]`;
2266
+ const duplicateTemplateErrorMessage = (templates) => `Templates must have unique names. Found duplicate template names: [${templates}]`;
2267
+ const duplicateCollectionErrorMessage = (collection) => `Collections must have unique names. Found duplicate collection names: [${collection}]`;
2248
2268
  const TypeName = [
2249
2269
  "string",
2250
2270
  "boolean",
@@ -2255,10 +2275,11 @@ const TypeName = [
2255
2275
  "reference",
2256
2276
  "rich-text"
2257
2277
  ];
2258
- const typeTypeError = `type must be one of ${TypeName.join(", ")}`;
2259
- const typeRequiredError = `type is required and must be one of ${TypeName.join(
2260
- ", "
2261
- )}`;
2278
+ const formattedTypes = ` - ${TypeName.join("\n - ")}`;
2279
+ const typeTypeError = `Invalid \`type\` property. \`type\` expected to be one of the following values:
2280
+ ${formattedTypes}`;
2281
+ const typeRequiredError = `Missing \`type\` property. Please add a \`type\` property with one of the following:
2282
+ ${formattedTypes}`;
2262
2283
  const Option = z.union(
2263
2284
  [
2264
2285
  z.string(),
@@ -2344,7 +2365,7 @@ const TinaFieldZod = z.lazy(() => {
2344
2365
  if (dups) {
2345
2366
  ctx.addIssue({
2346
2367
  code: z.ZodIssueCode.custom,
2347
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2368
+ message: duplicateFieldErrorMessage(dups)
2348
2369
  });
2349
2370
  }
2350
2371
  });
@@ -2354,21 +2375,21 @@ const TinaFieldZod = z.lazy(() => {
2354
2375
  invalid_type_error: typeTypeError,
2355
2376
  required_error: typeRequiredError
2356
2377
  }),
2357
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2378
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2358
2379
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2359
2380
  if (dups) {
2360
2381
  ctx.addIssue({
2361
2382
  code: z.ZodIssueCode.custom,
2362
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2383
+ message: duplicateFieldErrorMessage(dups)
2363
2384
  });
2364
2385
  }
2365
2386
  }),
2366
- templates: z.array(TemplateTemp).min(1).optional().superRefine((val, ctx) => {
2387
+ templates: z.array(TemplateTemp).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2367
2388
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2368
2389
  if (dups) {
2369
2390
  ctx.addIssue({
2370
2391
  code: z.ZodIssueCode.custom,
2371
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2392
+ message: duplicateTemplateErrorMessage(dups)
2372
2393
  });
2373
2394
  }
2374
2395
  })
@@ -2383,7 +2404,7 @@ const TinaFieldZod = z.lazy(() => {
2383
2404
  if (dups) {
2384
2405
  ctx.addIssue({
2385
2406
  code: z.ZodIssueCode.custom,
2386
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2407
+ message: duplicateTemplateErrorMessage(dups)
2387
2408
  });
2388
2409
  }
2389
2410
  })
@@ -2403,10 +2424,17 @@ const TinaFieldZod = z.lazy(() => {
2403
2424
  ],
2404
2425
  {
2405
2426
  errorMap: (issue, ctx) => {
2406
- var _a;
2427
+ var _a, _b;
2407
2428
  if (issue.code === "invalid_union_discriminator") {
2429
+ if (!((_a = ctx.data) == null ? void 0 : _a.type)) {
2430
+ return {
2431
+ message: `Missing \`type\` property in field \`${ctx.data.name}\`. Please add a \`type\` property with one of the following:
2432
+ ${formattedTypes}`
2433
+ };
2434
+ }
2408
2435
  return {
2409
- message: `Invalid \`type\` property. In the schema is 'type: ${(_a = ctx.data) == null ? void 0 : _a.type}' and expected one of ${TypeName.join(", ")}`
2436
+ message: `Invalid \`type\` property in field \`${ctx.data.name}\`. In the schema is 'type: ${(_b = ctx.data) == null ? void 0 : _b.type}' but expected one of the following:
2437
+ ${formattedTypes}`
2410
2438
  };
2411
2439
  }
2412
2440
  return {
@@ -2416,77 +2444,52 @@ const TinaFieldZod = z.lazy(() => {
2416
2444
  }
2417
2445
  ).superRefine((val, ctx) => {
2418
2446
  if (val.type === "string") {
2419
- if (val.isTitle) {
2420
- if (val.list) {
2421
- ctx.addIssue({
2422
- code: z.ZodIssueCode.custom,
2423
- message: `Can not have \`list: true\` when using \`isTitle\`. Error in value
2424
- ${JSON.stringify(
2425
- val,
2426
- null,
2427
- 2
2428
- )}
2429
- `
2430
- });
2431
- }
2432
- if (!val.required) {
2433
- ctx.addIssue({
2434
- code: z.ZodIssueCode.custom,
2435
- message: `Must have { required: true } when using \`isTitle\` Error in value
2436
- ${JSON.stringify(
2437
- val,
2438
- null,
2439
- 2
2440
- )}
2441
- `
2442
- });
2443
- }
2447
+ const stringifiedField = JSON.stringify(val, null, 2);
2448
+ if (val.isTitle && val.list) {
2449
+ ctx.addIssue({
2450
+ code: z.ZodIssueCode.custom,
2451
+ message: `\`list: true\` is not allowed when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2452
+ ${stringifiedField}`
2453
+ });
2444
2454
  }
2445
- if (val.uid) {
2455
+ if (val.isTitle && !val.required) {
2456
+ ctx.addIssue({
2457
+ code: z.ZodIssueCode.custom,
2458
+ message: `Property \`required: true\` is required when using \`isTitle\` for fields of \`type: string\`. Error found in field:
2459
+ ${stringifiedField}`
2460
+ });
2461
+ }
2462
+ if (val.uid && val.list) {
2446
2463
  if (val.list) {
2447
2464
  ctx.addIssue({
2448
2465
  code: z.ZodIssueCode.custom,
2449
- message: `Can not have \`list: true\` when using \`uid\`. Error in value
2450
- ${JSON.stringify(
2451
- val,
2452
- null,
2453
- 2
2454
- )}
2455
- `
2456
- });
2457
- }
2458
- if (!val.required) {
2459
- ctx.addIssue({
2460
- code: z.ZodIssueCode.custom,
2461
- message: `Must have { required: true } when using \`uid\` Error in value
2462
- ${JSON.stringify(
2463
- val,
2464
- null,
2465
- 2
2466
- )}
2467
- `
2466
+ message: `\`list: true\` is not allowed when using \`uid\` for fields of \`type: string\`. Error found in field:
2467
+ ${stringifiedField}`
2468
2468
  });
2469
2469
  }
2470
2470
  }
2471
+ if (val.uid && !val.required) {
2472
+ ctx.addIssue({
2473
+ code: z.ZodIssueCode.custom,
2474
+ message: `Property \`required: true\` is required when using \`uid\` for fields of \`type: string\`. Error found in field:
2475
+ ${stringifiedField}`
2476
+ });
2477
+ }
2471
2478
  }
2472
2479
  if (val.type === "object") {
2473
- const message = "Must provide one of templates or fields in your collection";
2474
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2475
- if (!isValid) {
2480
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2476
2481
  ctx.addIssue({
2477
2482
  code: z.ZodIssueCode.custom,
2478
- message
2483
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2484
+ });
2485
+ return false;
2486
+ }
2487
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2488
+ ctx.addIssue({
2489
+ code: z.ZodIssueCode.custom,
2490
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2479
2491
  });
2480
2492
  return false;
2481
- } else {
2482
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2483
- if (!isValid) {
2484
- ctx.addIssue({
2485
- code: z.ZodIssueCode.custom,
2486
- message
2487
- });
2488
- }
2489
- return isValid;
2490
2493
  }
2491
2494
  }
2492
2495
  return true;
@@ -2530,8 +2533,8 @@ const FORMATS = [
2530
2533
  ];
2531
2534
  const Template = z.object({
2532
2535
  label: z.string({
2533
- invalid_type_error: "label must be a string",
2534
- required_error: "label was not provided but is required"
2536
+ invalid_type_error: "Invalid data type for property `label`. Must be of type `string`",
2537
+ required_error: "Missing `label` property. Property `label` is required."
2535
2538
  }),
2536
2539
  name,
2537
2540
  fields: z.array(TinaFieldZod)
@@ -2541,7 +2544,7 @@ const Template = z.object({
2541
2544
  if (dups) {
2542
2545
  ctx.addIssue({
2543
2546
  code: z.ZodIssueCode.custom,
2544
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2547
+ message: duplicateFieldErrorMessage(dups)
2545
2548
  });
2546
2549
  }
2547
2550
  });
@@ -2551,7 +2554,7 @@ const CollectionBaseSchema = z.object({
2551
2554
  if (val === "relativePath") {
2552
2555
  ctx.addIssue({
2553
2556
  code: z.ZodIssueCode.custom,
2554
- message: `name cannot be 'relativePath'. 'relativePath' is a reserved field name.`
2557
+ message: "Invalid `name` property. `name` cannot be 'relativePath' as it is a reserved field name."
2555
2558
  });
2556
2559
  }
2557
2560
  }),
@@ -2559,7 +2562,7 @@ const CollectionBaseSchema = z.object({
2559
2562
  if (val === ".") {
2560
2563
  ctx.addIssue({
2561
2564
  code: z.ZodIssueCode.custom,
2562
- message: `path cannot be '.'. Please use '/' or '' instead. `
2565
+ message: "Invalid `path` property. `path` cannot be '.'. Please use '/' or '' instead."
2563
2566
  });
2564
2567
  }
2565
2568
  }),
@@ -2568,63 +2571,64 @@ const CollectionBaseSchema = z.object({
2568
2571
  isDetached: z.boolean().optional()
2569
2572
  });
2570
2573
  const TinaCloudCollection = CollectionBaseSchema.extend({
2571
- fields: z.array(TinaFieldZod).min(1).optional().superRefine((val, ctx) => {
2574
+ fields: z.array(TinaFieldZod).min(1, "Property `fields` cannot be empty.").optional().superRefine((val, ctx) => {
2572
2575
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2573
2576
  if (dups) {
2574
2577
  ctx.addIssue({
2575
2578
  code: z.ZodIssueCode.custom,
2576
- message: `Fields must have a unique name, duplicate field names: ${dups}`
2579
+ message: duplicateFieldErrorMessage(dups)
2577
2580
  });
2578
2581
  }
2579
- }).refine(
2580
- // It is valid if it is 0 or 1
2581
- (val) => {
2582
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2583
- return arr.length < 2;
2584
- },
2585
- {
2586
- message: "Fields can only have one use of `isTitle`"
2587
- }
2588
- ).refine(
2589
- // It is valid if it is 0 or 1
2590
- (val) => {
2591
- const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2592
- return arr.length < 2;
2593
- },
2594
- {
2595
- message: "Fields can only have one use of `uid`"
2596
- }
2597
- ).refine(
2598
- // It is valid if it is 0 or 1
2599
- (val) => {
2600
- const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2601
- return arr.length < 2;
2602
- },
2603
- {
2604
- message: "Fields can only have one use of `password` type"
2582
+ }).superRefine((val, ctx) => {
2583
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "string" && x.isTitle)) || [];
2584
+ if (arr.length > 1) {
2585
+ ctx.addIssue({
2586
+ code: z.ZodIssueCode.custom,
2587
+ message: `The following fields have the property \`isTitle\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`isTitle\`.`
2588
+ });
2589
+ }
2590
+ }).superRefine((val, ctx) => {
2591
+ const arr = (val == null ? void 0 : val.filter((x) => x.uid)) || [];
2592
+ if (arr.length > 2) {
2593
+ ctx.addIssue({
2594
+ code: z.ZodIssueCode.custom,
2595
+ message: `The following fields have the property \`uid\`: [${arr.map((field) => field.name).join(", ")}]. Only one can contain the property \`uid\`.`
2596
+ });
2597
+ }
2598
+ }).superRefine((val, ctx) => {
2599
+ const arr = (val == null ? void 0 : val.filter((x) => x.type === "password")) || [];
2600
+ if (arr.length > 2) {
2601
+ ctx.addIssue({
2602
+ code: z.ZodIssueCode.custom,
2603
+ message: `The following fields have \`type: password\`: [${arr.map((field) => field.name).join(", ")}]. Only one can be of \`type: password\`.`
2604
+ });
2605
2605
  }
2606
- ),
2607
- templates: z.array(Template).min(1).optional().superRefine((val, ctx) => {
2606
+ }),
2607
+ templates: z.array(Template).min(1, "Property `templates` cannot be empty.").optional().superRefine((val, ctx) => {
2608
2608
  const dups = findDuplicates(val == null ? void 0 : val.map((x) => x.name));
2609
2609
  if (dups) {
2610
2610
  ctx.addIssue({
2611
2611
  code: z.ZodIssueCode.custom,
2612
- message: `Templates must have a unique name, duplicate template names: ${dups}`
2612
+ message: duplicateFieldErrorMessage(dups)
2613
2613
  });
2614
2614
  }
2615
2615
  })
2616
- }).refine(
2617
- (val) => {
2618
- let isValid = Boolean(val == null ? void 0 : val.templates) || Boolean(val == null ? void 0 : val.fields);
2619
- if (!isValid) {
2620
- return false;
2621
- } else {
2622
- isValid = !((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields));
2623
- return isValid;
2624
- }
2625
- },
2626
- { message: "Must provide one of templates or fields in your collection" }
2627
- );
2616
+ }).superRefine((val, ctx) => {
2617
+ if (!(val == null ? void 0 : val.templates) && !(val == null ? void 0 : val.fields)) {
2618
+ ctx.addIssue({
2619
+ code: z.ZodIssueCode.custom,
2620
+ message: "Fields of `type: object` must have either `templates` or `fields` property."
2621
+ });
2622
+ return false;
2623
+ }
2624
+ if ((val == null ? void 0 : val.templates) && (val == null ? void 0 : val.fields)) {
2625
+ ctx.addIssue({
2626
+ code: z.ZodIssueCode.custom,
2627
+ message: "Fields of `type: object` must have either `templates` or `fields` property, not both."
2628
+ });
2629
+ return false;
2630
+ }
2631
+ });
2628
2632
  const TinaCloudSchemaZod = z.object({
2629
2633
  collections: z.array(TinaCloudCollection),
2630
2634
  config: tinaConfigZod.optional()
@@ -2634,14 +2638,14 @@ const TinaCloudSchemaZod = z.object({
2634
2638
  if (dups) {
2635
2639
  ctx.addIssue({
2636
2640
  code: z.ZodIssueCode.custom,
2637
- message: `${dups} are duplicate names in your collections. Collection names must be unique.`,
2641
+ message: duplicateCollectionErrorMessage(dups),
2638
2642
  fatal: true
2639
2643
  });
2640
2644
  }
2641
2645
  if (((_b = val.collections) == null ? void 0 : _b.filter((x) => x.isAuthCollection).length) > 1) {
2642
2646
  ctx.addIssue({
2643
2647
  code: z.ZodIssueCode.custom,
2644
- message: `Only one collection can be marked as isAuthCollection`,
2648
+ message: "Only one collection can be marked as `isAuthCollection`.",
2645
2649
  fatal: true
2646
2650
  });
2647
2651
  }
@@ -2649,7 +2653,7 @@ const TinaCloudSchemaZod = z.object({
2649
2653
  if (media && media.tina && media.loadCustomStore) {
2650
2654
  ctx.addIssue({
2651
2655
  code: z.ZodIssueCode.custom,
2652
- message: "can not have both loadCustomStore and tina. Must use one or the other",
2656
+ message: "Cannot have both `loadCustomStore` and `tina`. Must use one or the other.",
2653
2657
  fatal: true,
2654
2658
  path: ["config", "media"]
2655
2659
  });
@@ -2658,7 +2662,7 @@ const TinaCloudSchemaZod = z.object({
2658
2662
  if (search && search.tina && search.searchClient) {
2659
2663
  ctx.addIssue({
2660
2664
  code: z.ZodIssueCode.custom,
2661
- message: "can not have both searchClient and tina. Must use one or the other",
2665
+ message: "Cannot have both `searchClient` and `tina`. Must use one or the other.",
2662
2666
  fatal: true,
2663
2667
  path: ["config", "search"]
2664
2668
  });
@@ -2676,7 +2680,7 @@ const validateSchema = ({ schema }) => {
2676
2680
  } catch (e) {
2677
2681
  if (e instanceof ZodError) {
2678
2682
  const errors = parseZodError({ zodError: e });
2679
- throw new TinaSchemaValidationError(errors.join(", \n"));
2683
+ throw new TinaSchemaValidationError(errors.join("\n"));
2680
2684
  }
2681
2685
  throw new Error(e);
2682
2686
  }
@@ -28,6 +28,10 @@ export declare class TinaSchema {
28
28
  } & Schema);
29
29
  getIsTitleFieldName: (collection: string) => string;
30
30
  getCollectionsByName: (collectionNames: string[]) => Collection<true>[];
31
+ findReferences(name: string): Record<string, {
32
+ path: string[];
33
+ field: TinaField;
34
+ }[]>;
31
35
  getCollection: (collectionName: string) => Collection<true>;
32
36
  getCollections: () => Collection<true>[];
33
37
  getCollectionByFullPath: (filepath: string) => Collection<true>;
@@ -185,7 +185,7 @@ export type ReferenceField = (FieldGeneric<string, undefined, ReferenceFieldOpti
185
185
  export type PasswordField = (FieldGeneric<string, undefined> | FieldGeneric<string, false>) & BaseField & {
186
186
  type: 'password';
187
187
  };
188
- type toolbarItemName = 'heading' | 'link' | 'image' | 'quote' | 'ul' | 'ol' | 'code' | 'codeBlock' | 'bold' | 'italic' | 'raw' | 'embed' | 'mermaid';
188
+ type ToolbarOverrideType = 'heading' | 'link' | 'image' | 'quote' | 'ul' | 'ol' | 'code' | 'codeBlock' | 'bold' | 'italic' | 'raw' | 'embed' | 'mermaid' | 'table';
189
189
  type RichTextAst = {
190
190
  type: 'root';
191
191
  children: Record<string, unknown>[];
@@ -198,7 +198,7 @@ export type RichTextField<WithNamespace extends boolean = false> = (FieldGeneric
198
198
  * will be stored as frontmatter
199
199
  */
200
200
  isBody?: boolean;
201
- toolbarOverride?: toolbarItemName[];
201
+ toolbarOverride?: ToolbarOverrideType[];
202
202
  templates?: RichTextTemplate<WithNamespace>[];
203
203
  /**
204
204
  * By default, Tina parses markdown with MDX, this is a more strict parser
@@ -630,6 +630,7 @@ type Document = {
630
630
  relativePath: string;
631
631
  filename: string;
632
632
  extension: string;
633
+ hasReferences?: boolean;
633
634
  };
634
635
  };
635
636
  export interface UICollection<Form = any, CMS = any, TinaForm = any> {
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import { z } from 'zod';
5
2
  import type { TinaField as TinaFieldType } from '../types/index';
6
3
  export declare const TinaFieldZod: z.ZodType<TinaFieldType>;
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import { z } from 'zod';
5
2
  export declare const CollectionBaseSchema: z.ZodObject<{
6
3
  label: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ export declare const duplicateFieldErrorMessage: (fields: string) => string;
2
+ export declare const duplicateTemplateErrorMessage: (templates: string) => string;
3
+ export declare const duplicateCollectionErrorMessage: (collection: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/schema-tools",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "exports": {
@@ -23,16 +23,16 @@
23
23
  ]
24
24
  },
25
25
  "devDependencies": {
26
- "@types/jest": "^29.5.13",
26
+ "@types/jest": "^29.5.14",
27
27
  "@types/micromatch": "^4.0.9",
28
- "@types/react": "^18.3.10",
28
+ "@types/react": "^18.3.12",
29
29
  "@types/yup": "^0.29.14",
30
30
  "jest": "^29.7.0",
31
31
  "react": "^18.3.1",
32
32
  "ts-jest": "^29.2.5",
33
- "typescript": "^5.6.2",
33
+ "typescript": "^5.6.3",
34
34
  "yup": "^0.32.11",
35
- "@tinacms/scripts": "1.3.0"
35
+ "@tinacms/scripts": "1.3.1"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": ">=16.14.0",