@tinacms/schema-tools 0.2.0 → 0.2.1

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.d.ts CHANGED
@@ -11,6 +11,6 @@ See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
13
  export * from './schema';
14
- export * from './types';
14
+ export * from './types/index';
15
15
  export * from './validate';
16
16
  export * from './util/namer';
package/dist/index.es.js CHANGED
@@ -598,7 +598,7 @@ const ReferenceField = FieldWithList.extend({
598
598
  });
599
599
  const TinaFieldZod = z.lazy(() => {
600
600
  const TemplateTemp = z.object({
601
- label: z.string(),
601
+ label: z.string().optional(),
602
602
  name: nameProp,
603
603
  fields: z.array(TinaFieldZod),
604
604
  match: z.object({
package/dist/index.js CHANGED
@@ -625,7 +625,7 @@
625
625
  });
626
626
  const TinaFieldZod = z.z.lazy(() => {
627
627
  const TemplateTemp = z.z.object({
628
- label: z.z.string(),
628
+ label: z.z.string().optional(),
629
629
  name: nameProp,
630
630
  fields: z.z.array(TinaFieldZod),
631
631
  match: z.z.object({
@@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
10
  See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
- import { TinaCloudSchemaEnriched, TinaCloudSchemaBase, TinaCloudCollection, Templateable, Collectable, CollectionTemplateable } from '../types';
13
+ import { TinaCloudSchemaEnriched, TinaCloudSchemaBase, TinaCloudCollection, Templateable, Collectable, CollectionTemplateable } from '../types/index';
14
14
  declare type Version = {
15
15
  fullVersion: string;
16
16
  major: string;
@@ -49,8 +49,8 @@ export declare class TinaSchema {
49
49
  getGlobalTemplate: (templateName: string) => {
50
50
  label: string;
51
51
  name: string;
52
- ui?: import("../types").UICollection;
53
- fields: import("../types").TinaFieldInner<true>[];
52
+ ui?: import("../types/SchemaTypes").UICollection;
53
+ fields: import("../types/SchemaTypes").TinaFieldInner<true>[];
54
54
  namespace: string[];
55
55
  };
56
56
  getCollectionByFullPath: (filepath: string) => TinaCloudCollection<true>;
@@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
10
  See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
- import { TinaFieldEnriched } from '../types';
13
+ import { TinaFieldEnriched } from '../types/index';
14
14
  import { TinaSchema } from './TinaSchema';
15
15
  /**
16
16
  *
@@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
10
  See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
- import type { ResolveFormArgs } from '../types';
13
+ import type { ResolveFormArgs } from '../types/index';
14
14
  /**
15
15
  * Given a collection, basename, template and schema. This will transform the given information into a valid frontend form config
16
16
  */
@@ -16,4 +16,3 @@ limitations under the License.
16
16
 
17
17
  */
18
18
  export * from './SchemaTypes';
19
- export * from './SchemaTypes2';
@@ -0,0 +1,453 @@
1
+ /**
2
+ Copyright 2021 Forestry.io Holdings, Inc.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ */
13
+ /**
14
+ *
15
+ */
16
+ export interface UICollection {
17
+ /**
18
+ * Customize the way filenames are generated during content creation
19
+ */
20
+ filename?: {
21
+ /**
22
+ * A callback which receives form values as an argument. The return value
23
+ * here will be used as the filename (the extension is not necessary)
24
+ *
25
+ * eg:
26
+ * ```ts
27
+ * slugify: (values) => values.title.toLowerCase().split(" ").join("-")
28
+ * ```
29
+ */
30
+ slugify?: (values: Record<string, any>) => string;
31
+ /**
32
+ * When set to `true`, editors won't be able to modify the filename
33
+ */
34
+ readonly?: boolean;
35
+ };
36
+ /**
37
+ * Forms for this collection will be editable from the global sidebar rather than the form panel
38
+ */
39
+ global?: boolean | {
40
+ icon?: any;
41
+ layout: 'fullscreen' | 'popup';
42
+ };
43
+ /**
44
+ * Provide the path that your document is viewable on your site
45
+ *
46
+ * eg:
47
+ * ```ts
48
+ * router: ({ document }) => {
49
+ * return `blog-posts/${document._sys.filename}`;
50
+ * }
51
+ * ```
52
+ */
53
+ router?: (args: {
54
+ document: Document;
55
+ collection: Collection;
56
+ }) => string | undefined;
57
+ }
58
+ export declare type Option = string | {
59
+ label: string;
60
+ value: string;
61
+ };
62
+ import type React from 'react';
63
+ declare type Meta = {
64
+ active?: boolean;
65
+ dirty?: boolean;
66
+ error?: any;
67
+ };
68
+ declare type Component<Type, List> = (props: {
69
+ field: SchemaField & {
70
+ namespace: string[];
71
+ };
72
+ input: {
73
+ /**
74
+ * The full name of the field, for fields nested inside object
75
+ * fields, this will be the full path:
76
+ *
77
+ * `myObject.0.title`
78
+ */
79
+ name: string;
80
+ onBlur: (event?: React.FocusEvent<Type>) => void;
81
+ /**
82
+ * The value provided will be saved to the form so it
83
+ * should match the configured type:
84
+ *
85
+ * `input.onChange('some string')`
86
+ */
87
+ onChange: (event: React.ChangeEvent<Type>) => void;
88
+ onFocus: (event?: React.FocusEvent<Type>) => void;
89
+ type?: string;
90
+ value: List extends true ? Type[] : Type;
91
+ };
92
+ meta: Meta;
93
+ }) => any;
94
+ declare type UIField<Type, List extends boolean> = {
95
+ /**
96
+ * Override the label from parent object
97
+ */
98
+ label?: string;
99
+ /**
100
+ * Override the description from parent object
101
+ */
102
+ description?: string;
103
+ /**
104
+ * A React component which will be used in the Tina form. Be sure
105
+ * to import React into the config file.
106
+ *
107
+ * Note: Any Tailwind classes provided here will be compiled as part
108
+ * of the Tina stylesheet
109
+ *
110
+ * eg:
111
+ * ```tsx
112
+ * component: (props) => {
113
+ * const { input, field } = props
114
+ * return (
115
+ * <div className="my-4">
116
+ * <label
117
+ * htmlFor={input.name}
118
+ * className="block text-sm font-medium"
119
+ * >
120
+ * {field.name}
121
+ * </label>
122
+ * <div className="mt-1">
123
+ * <input
124
+ * id={input.name}
125
+ * className="py-2 px-4 block"
126
+ * type="text"
127
+ * {...input}
128
+ * />
129
+ * </div>
130
+ * </div>
131
+ * )
132
+ * }
133
+ * ```
134
+ *
135
+ * Note: If the form has already been registered with the cms, you
136
+ * can provide it's name here (eg. `textarea`)
137
+ */
138
+ component?: Component<Type, List> | string | null;
139
+ /**
140
+ * Optional: Prepare data for use in the component. This is useful
141
+ * if you don't have access to the component directly
142
+ */
143
+ parse?: (value: List extends true ? Type[] : Type, name: string, field: Field) => List extends true ? Type[] : Type;
144
+ /**
145
+ * Optional: Prepare data for saving. This is useful
146
+ * if you don't have access to the component directly
147
+ */
148
+ format?: (value: Type, name: string, field: Field) => List extends true ? Type[] : Type;
149
+ /**
150
+ * Optional: Return undefined when valid. Return a string or an object when there are errors.
151
+ *
152
+ * ```ts
153
+ * validate: (value) => {
154
+ * if(value.length > 40){
155
+ * return 'Title cannot be more than 40 characters long'
156
+ * }
157
+ * }
158
+ * ```
159
+ */
160
+ validate?(value: List extends true ? Type[] : Type, allValues: {
161
+ [key: string]: any;
162
+ }, meta: Meta, field: UIField<Type, List>): (List extends true ? Type[] : Type) | undefined | void;
163
+ /**
164
+ * @deprecated use `defaultItem` at the collection level instead
165
+ */
166
+ defaultValue?: List extends true ? Type[] : Type;
167
+ };
168
+ declare type FieldGeneric<Type, List extends boolean | undefined, ExtraFieldUIProps = {}> = List extends true ? {
169
+ list: true;
170
+ ui?: UIField<Type, true> & ExtraFieldUIProps;
171
+ } : List extends false ? {
172
+ list: false;
173
+ ui?: UIField<Type, false> & ExtraFieldUIProps;
174
+ } : {
175
+ list?: never;
176
+ ui?: UIField<Type, false> & ExtraFieldUIProps;
177
+ };
178
+ export interface BaseField {
179
+ label?: string;
180
+ required?: boolean;
181
+ name: string;
182
+ }
183
+ export declare type StringField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
184
+ type: 'string';
185
+ isTitle?: boolean;
186
+ options?: Option[];
187
+ };
188
+ export declare type NumberField = (FieldGeneric<number, undefined> | FieldGeneric<number, true> | FieldGeneric<number, false>) & BaseField & {
189
+ type: 'number';
190
+ };
191
+ export declare type BooleanField = (FieldGeneric<boolean, undefined> | FieldGeneric<boolean, true> | FieldGeneric<boolean, false>) & BaseField & {
192
+ type: 'boolean';
193
+ };
194
+ export declare type DateTimeField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
195
+ type: 'datetime';
196
+ };
197
+ export declare type ImageField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
198
+ type: 'image';
199
+ };
200
+ export declare type ReferenceField = (FieldGeneric<string, undefined> | FieldGeneric<string, false>) & BaseField & {
201
+ type: 'reference';
202
+ /**
203
+ * The names of the collections this field can use as a reference
204
+ * ```ts
205
+ * {
206
+ * type: 'reference',
207
+ * name: 'author',
208
+ * collections: ['author'],
209
+ * }
210
+ * ```
211
+ */
212
+ collections: string[];
213
+ };
214
+ declare type RichTextAst = {
215
+ type: 'root';
216
+ children: Record<string, unknown>[];
217
+ };
218
+ export declare type RichTextField = (FieldGeneric<RichTextAst, undefined> | FieldGeneric<RichTextAst, false>) & BaseField & {
219
+ type: 'rich-text';
220
+ /**
221
+ * When using Markdown or MDX formats, this field's value
222
+ * will be saved to the markdown body, while all other values
223
+ * will be stored as frontmatter
224
+ */
225
+ isBody?: boolean;
226
+ templates?: (Template & {
227
+ inline?: boolean;
228
+ /**
229
+ * If you have some custom shortcode logic in your markdown,
230
+ * you can specify it in the 'match' property and Tina will
231
+ * handle it as if it were a jsx element:
232
+ *
233
+ * ```
234
+ * # This is my markdown, it uses some custom shortcode
235
+ * syntax {{ myshortcode title="hello!" }}.
236
+ *
237
+ * {
238
+ * match: {
239
+ * start: "{{"
240
+ * end: "}}"
241
+ * }
242
+ * }
243
+ * ```
244
+ */
245
+ match?: {
246
+ start: string;
247
+ end: string;
248
+ };
249
+ })[];
250
+ };
251
+ declare type DefaultItem<ReturnType> = ReturnType | (() => ReturnType);
252
+ declare type ExtraFieldUIProps = {
253
+ /**
254
+ * Override the properties passed to the field
255
+ * component. This is mostly useful for controlling
256
+ * the display value via callback on `itemProps.label`
257
+ */
258
+ itemProps?(item: Record<string, any>): {
259
+ key?: string;
260
+ /**
261
+ * Control the display value when object
262
+ * items are shown in a compact list, eg:
263
+ *
264
+ * ```ts
265
+ * itemProps: (values) => ({
266
+ * label: values?.title || 'Showcase Item',
267
+ * }),
268
+ * ```
269
+ */
270
+ label?: string;
271
+ };
272
+ /**
273
+ * The value will be used when a new object is inserted, eg:
274
+ *
275
+ * ```ts
276
+ * {
277
+ * title: "My Headline",
278
+ * description: "Some description"
279
+ * }
280
+ * ```
281
+ *
282
+ * Note: when supplying a value for a `rich-text` field, you must supply
283
+ * the the value as an object.
284
+ * ```ts
285
+ * {
286
+ * title: "My Headline",
287
+ * description: "Some description"
288
+ * // This is field a rich-text field
289
+ * body: {
290
+ * type: "root",
291
+ * children: [{
292
+ * type: "p",
293
+ * children: [{
294
+ * type: "text",
295
+ * value: "This is some placeholder text"
296
+ * }]
297
+ * }]
298
+ * }
299
+ * }
300
+ * ```
301
+ *
302
+ */
303
+ defaultItem?: DefaultItem<Record<string, any>>;
304
+ };
305
+ export declare type ObjectField = (FieldGeneric<string, undefined> | FieldGeneric<string, true, ExtraFieldUIProps> | FieldGeneric<string, false>) & BaseField & ({
306
+ type: 'object';
307
+ fields: Field[];
308
+ templates?: undefined;
309
+ } | {
310
+ type: 'object';
311
+ fields?: undefined;
312
+ templates: Template[];
313
+ });
314
+ declare type Field = StringField | NumberField | BooleanField | DateTimeField | ImageField | ReferenceField | RichTextField | ObjectField;
315
+ declare type SchemaField = Field;
316
+ export type { SchemaField };
317
+ export interface Template {
318
+ label?: string;
319
+ name: string;
320
+ fields: Field[];
321
+ }
322
+ export interface FieldCollection {
323
+ label?: string;
324
+ name: string;
325
+ path: string;
326
+ format?: 'json' | 'md' | 'markdown' | 'mdx';
327
+ ui?: UICollection;
328
+ templates?: never;
329
+ /**
330
+ * Fields define the shape of the content and the user input.
331
+ *
332
+ * https://tina.io/docs/reference/fields/
333
+ */
334
+ fields: Field[];
335
+ }
336
+ export interface TemplateCollection {
337
+ label?: string;
338
+ name: string;
339
+ path: string;
340
+ format?: 'json' | 'md' | 'markdown' | 'mdx';
341
+ ui?: UICollection;
342
+ /**
343
+ * In most cases, just using fields is enough, however templates can be used when there are multiple variants of the same collection or object. For example in a "page" collection there might be a need for a marketing page template and a content page template, both under the collection "page".
344
+ *
345
+ * https://tina.io/docs/reference/templates/
346
+ */
347
+ templates?: Template[];
348
+ fields?: never;
349
+ }
350
+ export declare type Collection = FieldCollection | TemplateCollection;
351
+ export interface Schema {
352
+ /**
353
+ * Collections represent a type of content (EX, blog post, page, author, etc). We recommend using singular naming in a collection (Ex: use post and not posts).
354
+ *
355
+ * https://tina.io/docs/reference/collections/
356
+ */
357
+ collections: Collection[];
358
+ }
359
+ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined> {
360
+ /**
361
+ * The Schema is used to define the shape of the content.
362
+ *
363
+ * https://tina.io/docs/reference/schema/
364
+ */
365
+ schema: Schema;
366
+ /**
367
+ * The base branch to pull content from. Note that this is ignored for local development
368
+ */
369
+ branch: string | null;
370
+ /**
371
+ * Your clientId from app.tina.io
372
+ */
373
+ clientId: string | null;
374
+ /**
375
+ * Your read only token from app.tina.io
376
+ */
377
+ token: string | null;
378
+ /**
379
+ * Configurations for the autogenerated GraphQL HTTP client
380
+ */
381
+ client?: {
382
+ /**
383
+ * Autogenerated queries will traverse references to a given depth
384
+ * @default 2
385
+ */
386
+ referenceDepth?: number;
387
+ };
388
+ /**
389
+ * Tina is compiled as a single-page app and placed in the public directory
390
+ * of your application.
391
+ */
392
+ build: {
393
+ /**
394
+ * The folder where your application stores assets, eg. `"public"`
395
+ */
396
+ publicFolder: string;
397
+ /**
398
+ * The value specified here will determine the path when visiting the TinaCMS dashboard.
399
+ *
400
+ * Eg. `"admin"` will be viewable at `[your-development-url]/admin/index.html`
401
+ *
402
+ * Note that for most framworks you can omit the `index.html` portion, for Next.js see the [rewrites section](https://nextjs.org/docs/api-reference/next.config.js/rewrites)
403
+ */
404
+ outputFolder: string;
405
+ };
406
+ media?: {
407
+ /**
408
+ * Load a media store like Cloudinary
409
+ *
410
+ * ```ts
411
+ * loadCustomStore = async () => {
412
+ * const pack = await import("next-tinacms-cloudinary");
413
+ * return pack.TinaCloudCloudinaryMediaStore;
414
+ * }
415
+ * ```
416
+ */
417
+ loadCustomStore: () => Promise<Store>;
418
+ tina?: never;
419
+ } | {
420
+ /**
421
+ * Use Git-backed assets for media, these values will
422
+ * [Learn more](https://tina.io/docs/reference/media/repo-based/)
423
+ */
424
+ tina: {
425
+ /**
426
+ * The folder where your application stores assets, eg. `"public"`
427
+ */
428
+ publicFolder: string;
429
+ /**
430
+ * The root folder for media managed by Tina. For example, `"uploads"`
431
+ * would store content in `"<my-public-folder>/uploads"`
432
+ */
433
+ mediaRoot: string;
434
+ };
435
+ loadCustomStore?: never;
436
+ };
437
+ /**
438
+ * Used to override the default Tina Cloud API URL
439
+ *
440
+ * [mostly for internal use only]
441
+ */
442
+ tinaioConfig?: {
443
+ assetsApiUrlOverride?: string;
444
+ frontendUrlOverride?: string;
445
+ identityApiUrlOverride?: string;
446
+ contentApiUrlOverride?: string;
447
+ };
448
+ cmsCallback?: CMSCallback;
449
+ formifyCallback?: FormifyCallback;
450
+ documentCreatorCallback?: DocumentCreatorCallback;
451
+ }
452
+ export declare type TinaCMSConfig<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined> = Config<CMSCallback, FormifyCallback, DocumentCreatorCallback, Store>;
453
+ export {};
@@ -0,0 +1,462 @@
1
+ /**
2
+ Copyright 2021 Forestry.io Holdings, Inc.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ */
13
+ declare type Doc = {
14
+ _sys: {
15
+ title?: string;
16
+ template: string;
17
+ breadcrumbs: string[];
18
+ path: string;
19
+ basename: string;
20
+ relativePath: string;
21
+ filename: string;
22
+ extension: string;
23
+ };
24
+ };
25
+ /**
26
+ *
27
+ */
28
+ export interface UICollection {
29
+ filename?: {
30
+ /**
31
+ * A callback which receives form values as an argument. The return value
32
+ * here will be used as the filename (the extension is not necessary)
33
+ *
34
+ * eg:
35
+ * ```ts
36
+ * slugify: (values) => values.title.toLowerCase().split(" ").join("-")
37
+ * ```
38
+ */
39
+ slugify?: (values: Record<string, any>) => string;
40
+ /**
41
+ * When set to `true`, editors won't be able to modify the filename
42
+ */
43
+ readonly?: boolean;
44
+ };
45
+ /**
46
+ * Forms for this collection will be editable from the global sidebar rather than the form panel
47
+ */
48
+ global?: boolean | {
49
+ icon?: any;
50
+ layout: 'fullscreen' | 'popup';
51
+ };
52
+ /**
53
+ * Provide the path that your document is viewable on your site
54
+ *
55
+ * eg:
56
+ * ```ts
57
+ * router: ({ document }) => {
58
+ * return `blog-posts/${document._sys.filename}`;
59
+ * }
60
+ * ```
61
+ */
62
+ router?: (args: {
63
+ document: Doc;
64
+ collection: Collection;
65
+ }) => string | undefined;
66
+ }
67
+ export declare type Option = string | {
68
+ label: string;
69
+ value: string;
70
+ };
71
+ import type React from 'react';
72
+ declare type Meta = {
73
+ active?: boolean;
74
+ dirty?: boolean;
75
+ error?: any;
76
+ };
77
+ declare type Component<Type, List> = (props: {
78
+ field: SchemaField & {
79
+ namespace: string[];
80
+ };
81
+ input: {
82
+ /**
83
+ * The full name of the field, for fields nested inside object
84
+ * fields, this will be the full path:
85
+ *
86
+ * `myObject.0.title`
87
+ */
88
+ name: string;
89
+ onBlur: (event?: React.FocusEvent<Type>) => void;
90
+ /**
91
+ * The value provided will be saved to the form so it
92
+ * should match the configured type:
93
+ *
94
+ * `input.onChange('some string')`
95
+ */
96
+ onChange: (event: React.ChangeEvent<Type>) => void;
97
+ onFocus: (event?: React.FocusEvent<Type>) => void;
98
+ type?: string;
99
+ value: List extends true ? Type[] : Type;
100
+ };
101
+ meta: Meta;
102
+ }) => any;
103
+ declare type UIField<Type, List extends boolean> = {
104
+ /**
105
+ * Override the label from parent object
106
+ */
107
+ label?: string;
108
+ /**
109
+ * Override the description from parent object
110
+ */
111
+ description?: string;
112
+ /**
113
+ * A React component which will be used in the Tina form. Be sure
114
+ * to import React into the config file.
115
+ *
116
+ * Note: Any Tailwind classes provided here will be compiled as part
117
+ * of the Tina stylesheet
118
+ *
119
+ * eg:
120
+ * ```tsx
121
+ * component: (props) => {
122
+ * const { input, field } = props
123
+ * return (
124
+ * <div className="my-4">
125
+ * <label
126
+ * htmlFor={input.name}
127
+ * className="block text-sm font-medium"
128
+ * >
129
+ * {field.name}
130
+ * </label>
131
+ * <div className="mt-1">
132
+ * <input
133
+ * id={input.name}
134
+ * className="py-2 px-4 block"
135
+ * type="text"
136
+ * {...input}
137
+ * />
138
+ * </div>
139
+ * </div>
140
+ * )
141
+ * }
142
+ * ```
143
+ *
144
+ * Note: If the form has already been registered with the cms, you
145
+ * can provide it's name here (eg. `textarea`)
146
+ */
147
+ component?: Component<Type, List> | string | null;
148
+ /**
149
+ * Optional: Prepare data for use in the component. This is useful
150
+ * if you don't have access to the component directly
151
+ */
152
+ parse?: (value: List extends true ? Type[] : Type, name: string, field: Field) => List extends true ? Type[] : Type;
153
+ /**
154
+ * Optional: Prepare data for saving. This is useful
155
+ * if you don't have access to the component directly
156
+ */
157
+ format?: (value: Type, name: string, field: Field) => List extends true ? Type[] : Type;
158
+ /**
159
+ * Optional: Return undefined when valid. Return a string or an object when there are errors.
160
+ *
161
+ * ```ts
162
+ * validate: (value) => {
163
+ * if(value.length > 40){
164
+ * return 'Title cannot be more than 40 characters long'
165
+ * }
166
+ * }
167
+ * ```
168
+ */
169
+ validate?(value: List extends true ? Type[] : Type, allValues: {
170
+ [key: string]: any;
171
+ }, meta: Meta, field: UIField<Type, List>): (List extends true ? Type[] : Type) | undefined | void;
172
+ /**
173
+ * @deprecated use `defaultItem` at the collection level instead
174
+ */
175
+ defaultValue?: List extends true ? Type[] : Type;
176
+ };
177
+ declare type FieldGeneric<Type, List extends boolean | undefined, ExtraFieldUIProps = {}> = List extends true ? {
178
+ list: true;
179
+ ui?: UIField<Type, true> & ExtraFieldUIProps;
180
+ } : List extends false ? {
181
+ list?: false;
182
+ ui?: UIField<Type, false> & ExtraFieldUIProps;
183
+ } : {
184
+ list?: undefined;
185
+ ui?: UIField<Type, false> & ExtraFieldUIProps;
186
+ };
187
+ export interface BaseField {
188
+ label?: string;
189
+ required?: boolean;
190
+ name: string;
191
+ description?: string;
192
+ }
193
+ export declare type StringField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
194
+ type: 'string';
195
+ isTitle?: boolean;
196
+ options?: Option[];
197
+ };
198
+ export declare type NumberField = (FieldGeneric<number, undefined> | FieldGeneric<number, true> | FieldGeneric<number, false>) & BaseField & {
199
+ type: 'number';
200
+ };
201
+ export declare type BooleanField = (FieldGeneric<boolean, undefined> | FieldGeneric<boolean, true> | FieldGeneric<boolean, false>) & BaseField & {
202
+ type: 'boolean';
203
+ };
204
+ declare type DateFormatProps = {
205
+ /**
206
+ * Customize the way the format is rendered
207
+ * ```
208
+ * dateFormat: 'YYYY MM DD'
209
+ * ```
210
+ */
211
+ dateFormat?: string;
212
+ timeFormat?: string;
213
+ };
214
+ export declare type DateTimeField = (FieldGeneric<string, undefined, DateFormatProps> | FieldGeneric<string, true, DateFormatProps> | FieldGeneric<string, false, DateFormatProps>) & BaseField & {
215
+ type: 'datetime';
216
+ };
217
+ export declare type ImageField = (FieldGeneric<string, undefined> | FieldGeneric<string, true> | FieldGeneric<string, false>) & BaseField & {
218
+ type: 'image';
219
+ };
220
+ export declare type ReferenceField = (FieldGeneric<string, undefined> | FieldGeneric<string, false>) & BaseField & {
221
+ type: 'reference';
222
+ /**
223
+ * The names of the collections this field can use as a reference
224
+ * ```ts
225
+ * {
226
+ * type: 'reference',
227
+ * name: 'author',
228
+ * collections: ['author'],
229
+ * }
230
+ * ```
231
+ */
232
+ collections: string[];
233
+ };
234
+ declare type RichTextAst = {
235
+ type: 'root';
236
+ children: Record<string, unknown>[];
237
+ };
238
+ export declare type RichTextField = (FieldGeneric<RichTextAst, undefined> | FieldGeneric<RichTextAst, false>) & BaseField & {
239
+ type: 'rich-text';
240
+ /**
241
+ * When using Markdown or MDX formats, this field's value
242
+ * will be saved to the markdown body, while all other values
243
+ * will be stored as frontmatter
244
+ */
245
+ isBody?: boolean;
246
+ templates?: (Template & {
247
+ inline?: boolean;
248
+ /**
249
+ * If you have some custom shortcode logic in your markdown,
250
+ * you can specify it in the 'match' property and Tina will
251
+ * handle it as if it were a jsx element:
252
+ *
253
+ * ```
254
+ * # This is my markdown, it uses some custom shortcode
255
+ * syntax {{ myshortcode title="hello!" }}.
256
+ *
257
+ * {
258
+ * match: {
259
+ * start: "{{"
260
+ * end: "}}"
261
+ * }
262
+ * }
263
+ * ```
264
+ */
265
+ match?: {
266
+ start: string;
267
+ end: string;
268
+ };
269
+ })[];
270
+ };
271
+ declare type DefaultItem<ReturnType> = ReturnType | (() => ReturnType);
272
+ declare type ObjectUiProps = {
273
+ visualSelector?: boolean;
274
+ };
275
+ export declare type ObjectField = (FieldGeneric<string, undefined, ObjectUiProps> | FieldGeneric<string, true, ObjectUiProps> | FieldGeneric<string, false, ObjectUiProps>) & BaseField & ({
276
+ type: 'object';
277
+ fields: Field[];
278
+ templates?: undefined;
279
+ ui?: Template['ui'];
280
+ } | {
281
+ type: 'object';
282
+ fields?: undefined;
283
+ templates: Template[];
284
+ });
285
+ declare type Field = StringField | NumberField | BooleanField | DateTimeField | ImageField | ReferenceField | RichTextField | ObjectField;
286
+ declare type SchemaField = Field;
287
+ export type { SchemaField };
288
+ export interface Template {
289
+ label?: string;
290
+ name: string;
291
+ ui?: {
292
+ /**
293
+ * Override the properties passed to the field
294
+ * component. This is mostly useful for controlling
295
+ * the display value via callback on `itemProps.label`
296
+ */
297
+ itemProps?(item: Record<string, any>): {
298
+ key?: string;
299
+ /**
300
+ * Control the display value when object
301
+ * items are shown in a compact list, eg:
302
+ *
303
+ * ```ts
304
+ * itemProps: (values) => ({
305
+ * label: values?.title || 'Showcase Item',
306
+ * }),
307
+ * ```
308
+ */
309
+ label?: string;
310
+ };
311
+ defaultItem?: DefaultItem<Record<string, any>>;
312
+ /**
313
+ * When used in relation to the `visualSelector`,
314
+ * provide an image URL to be used as the preview
315
+ * in the blocks selector menu
316
+ */
317
+ previewSrc?: string;
318
+ };
319
+ fields: Field[];
320
+ }
321
+ export interface FieldCollection {
322
+ label?: string;
323
+ name: string;
324
+ path: string;
325
+ format?: 'json' | 'md' | 'markdown' | 'mdx';
326
+ ui?: UICollection & {
327
+ defaultItem?: DefaultItem<Record<string, any>>;
328
+ };
329
+ /**
330
+ * @deprecated - use `ui.defaultItem` instead
331
+ */
332
+ defaultItem?: DefaultItem<Record<string, any>>;
333
+ templates?: never;
334
+ /**
335
+ * Fields define the shape of the content and the user input.
336
+ *
337
+ * https://tina.io/docs/reference/fields/
338
+ */
339
+ fields: Field[];
340
+ }
341
+ export interface TemplateCollection {
342
+ label?: string;
343
+ name: string;
344
+ path: string;
345
+ format?: 'json' | 'md' | 'markdown' | 'mdx';
346
+ ui?: UICollection;
347
+ /**
348
+ * @deprecated - use `ui.defaultItem` on the each `template` instead
349
+ */
350
+ defaultItem?: DefaultItem<Record<string, any>>;
351
+ /**
352
+ * In most cases, just using fields is enough, however templates can be used when there are multiple variants of the same collection or object. For example in a "page" collection there might be a need for a marketing page template and a content page template, both under the collection "page".
353
+ *
354
+ * https://tina.io/docs/reference/templates/
355
+ */
356
+ templates?: Template[];
357
+ fields?: never;
358
+ }
359
+ export declare type Collection = FieldCollection | TemplateCollection;
360
+ export interface Schema {
361
+ /**
362
+ * Collections represent a type of content (EX, blog post, page, author, etc). We recommend using singular naming in a collection (Ex: use post and not posts).
363
+ *
364
+ * https://tina.io/docs/reference/collections/
365
+ */
366
+ collections: Collection[];
367
+ }
368
+ export interface Config<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined> {
369
+ /**
370
+ * The Schema is used to define the shape of the content.
371
+ *
372
+ * https://tina.io/docs/reference/schema/
373
+ */
374
+ schema: Schema;
375
+ /**
376
+ * The base branch to pull content from. Note that this is ignored for local development
377
+ */
378
+ branch: string | null;
379
+ /**
380
+ * Your clientId from app.tina.io
381
+ */
382
+ clientId: string | null;
383
+ /**
384
+ * Your read only token from app.tina.io
385
+ */
386
+ token: string | null;
387
+ /**
388
+ * Configurations for the autogenerated GraphQL HTTP client
389
+ */
390
+ client?: {
391
+ /**
392
+ * Autogenerated queries will traverse references to a given depth
393
+ * @default 2
394
+ */
395
+ referenceDepth?: number;
396
+ };
397
+ /**
398
+ * Tina is compiled as a single-page app and placed in the public directory
399
+ * of your application.
400
+ */
401
+ build: {
402
+ /**
403
+ * The folder where your application stores assets, eg. `"public"`
404
+ */
405
+ publicFolder: string;
406
+ /**
407
+ * The value specified here will determine the path when visiting the TinaCMS dashboard.
408
+ *
409
+ * Eg. `"admin"` will be viewable at `[your-development-url]/admin/index.html`
410
+ *
411
+ * Note that for most framworks you can omit the `index.html` portion, for Next.js see the [rewrites section](https://nextjs.org/docs/api-reference/next.config.js/rewrites)
412
+ */
413
+ outputFolder: string;
414
+ };
415
+ media?: {
416
+ /**
417
+ * Load a media store like Cloudinary
418
+ *
419
+ * ```ts
420
+ * loadCustomStore = async () => {
421
+ * const pack = await import("next-tinacms-cloudinary");
422
+ * return pack.TinaCloudCloudinaryMediaStore;
423
+ * }
424
+ * ```
425
+ */
426
+ loadCustomStore: () => Promise<Store>;
427
+ tina?: never;
428
+ } | {
429
+ /**
430
+ * Use Git-backed assets for media, these values will
431
+ * [Learn more](https://tina.io/docs/reference/media/repo-based/)
432
+ */
433
+ tina: {
434
+ /**
435
+ * The folder where your application stores assets, eg. `"public"`
436
+ */
437
+ publicFolder: string;
438
+ /**
439
+ * The root folder for media managed by Tina. For example, `"uploads"`
440
+ * would store content in `"<my-public-folder>/uploads"`
441
+ */
442
+ mediaRoot: string;
443
+ };
444
+ loadCustomStore?: never;
445
+ };
446
+ /**
447
+ * Used to override the default Tina Cloud API URL
448
+ *
449
+ * [mostly for internal use only]
450
+ */
451
+ tinaioConfig?: {
452
+ assetsApiUrlOverride?: string;
453
+ frontendUrlOverride?: string;
454
+ identityApiUrlOverride?: string;
455
+ contentApiUrlOverride?: string;
456
+ };
457
+ cmsCallback?: CMSCallback;
458
+ formifyCallback?: FormifyCallback;
459
+ documentCreatorCallback?: DocumentCreatorCallback;
460
+ }
461
+ export declare type TinaCMSConfig<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined> = Config<CMSCallback, FormifyCallback, DocumentCreatorCallback, Store>;
462
+ export {};
@@ -0,0 +1 @@
1
+
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ (function(factory) {
2
+ typeof define === "function" && define.amd ? define(factory) : factory();
3
+ })(function() {
4
+ "use strict";
5
+ });
@@ -10,11 +10,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
10
  See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
- import { TinaCloudSchema } from '../types';
13
+ import { Schema } from '../types';
14
+ import { TinaCloudSchema } from '../types/index';
14
15
  export { validateTinaCloudSchemaConfig } from './tinaCloudSchemaConfig';
15
16
  export declare class TinaSchemaValidationError extends Error {
16
17
  constructor(message: any);
17
18
  }
18
19
  export declare const validateSchema: ({ schema, }: {
19
- schema: TinaCloudSchema<false>;
20
+ schema: Schema | TinaCloudSchema<false>;
20
21
  }) => void;
@@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
10
  See the License for the specific language governing permissions and
11
11
  limitations under the License.
12
12
  */
13
- import { TinaCloudSchemaConfig } from '../types';
13
+ import { TinaCloudSchemaConfig } from '../types/index';
14
14
  import z from 'zod';
15
15
  export declare const tinaConfigZod: z.ZodObject<{
16
16
  client: z.ZodOptional<z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/schema-tools",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.es.js",
6
6
  "exports": {
@@ -8,6 +8,11 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "import": "./dist/index.es.js",
10
10
  "require": "./dist/index.js"
11
+ },
12
+ "./dist/types": {
13
+ "types": "./dist/types.d.ts",
14
+ "import": "./dist/types.es.js",
15
+ "require": "./dist/types.js"
11
16
  }
12
17
  },
13
18
  "typings": "dist/index.d.ts",
@@ -19,6 +24,9 @@
19
24
  "entryPoints": [
20
25
  {
21
26
  "name": "src/index.ts"
27
+ },
28
+ {
29
+ "name": "src/types.ts"
22
30
  }
23
31
  ]
24
32
  },
@@ -1,101 +0,0 @@
1
- /**
2
- Copyright 2021 Forestry.io Holdings, Inc.
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
- http://www.apache.org/licenses/LICENSE-2.0
7
- Unless required by applicable law or agreed to in writing, software
8
- distributed under the License is distributed on an "AS IS" BASIS,
9
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
- See the License for the specific language governing permissions and
11
- limitations under the License.
12
- */
13
- import { TinaCloudSchema } from './SchemaTypes';
14
- /**
15
- * Used with `defineStaticConfig`
16
- *
17
- * These are mostly similar types as whats in `schemaTypes`
18
- * but since those have gone through several iterations
19
- * they're pretty messy. These should act as the happy path
20
- * for iframe/standalone setups which we hope to eventually
21
- * make the default/only path for all Tina users.
22
- */
23
- export declare type TinaCMSConfig<CMSCallback = undefined, FormifyCallback = undefined, DocumentCreatorCallback = undefined, Store = undefined> = {
24
- schema: TinaCloudSchema<false>;
25
- /**
26
- * The base branch to pull content from. Note that this is ignored for local development
27
- */
28
- branch: string | null;
29
- /**
30
- * Your clientId from app.tina.io
31
- */
32
- clientId: string | null;
33
- /**
34
- * Your read only token from app.tina.io
35
- */
36
- token: string | null;
37
- /**
38
- * Configurations for the autogenerated GraphQL HTTP client
39
- */
40
- client?: {
41
- /**
42
- * Autogenerated queries will traverse references to a given depth
43
- * @default 2
44
- */
45
- referenceDepth?: number;
46
- };
47
- build: {
48
- /**
49
- * The folder where your application stores assets, eg. `"public"`
50
- */
51
- publicFolder: string;
52
- /**
53
- * TinaCMS is shipped as a single-page app, the value specified here will
54
- * determine the path when visiting the TinaCMS dashboard.
55
- *
56
- * Eg. `"admin"` will be viewable at `[your-development-url]/admin/index.html`
57
- */
58
- outputFolder: string;
59
- };
60
- media?: {
61
- /**
62
- * Load a media store like Cloudinary
63
- *
64
- * ```ts
65
- * loadCustomStore = async () => {
66
- * const pack = await import("next-tinacms-cloudinary");
67
- * return pack.TinaCloudCloudinaryMediaStore;
68
- * }
69
- * ```
70
- */
71
- loadCustomStore: () => Promise<Store>;
72
- tina?: never;
73
- } | {
74
- /**
75
- * Use Git-backed assets for media, these values will
76
- * [Learn more](https://tina.io/docs/reference/media/repo-based/)
77
- */
78
- tina: {
79
- /**
80
- * The folder where your application stores assets, eg. `"public"`
81
- */
82
- publicFolder: string;
83
- /**
84
- * The root folder for media managed by Tina. For example, `"uploads"`
85
- * would store content in `"<my-public-folder>/uploads"`
86
- */
87
- mediaRoot: string;
88
- };
89
- loadCustomStore?: never;
90
- };
91
- tinaioConfig?: {
92
- assetsApiUrlOverride?: string;
93
- frontendUrlOverride?: string;
94
- identityApiUrlOverride?: string;
95
- contentApiUrlOverride?: string;
96
- };
97
- cmsCallback?: CMSCallback;
98
- formifyCallback?: FormifyCallback;
99
- documentCreatorCallback?: DocumentCreatorCallback;
100
- };
101
- export {};