@portabletext/sanity-bridge 0.0.0
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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/index.cjs +255 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
- package/src/index.ts +6 -0
- package/src/key-generator.ts +32 -0
- package/src/portable-text-member-schema-types-to-schema.ts +62 -0
- package/src/portable-text-member-schema-types.ts +138 -0
- package/src/schema-definition-to-portable-text-member-schema-types.ts +151 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 - 2024 Sanity.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `@portabletext/sanity-bridge`
|
|
2
|
+
|
|
3
|
+
A TypeScript library for converting between Sanity schemas and Portable Text schemas, enabling seamless integration between Sanity CMS and Portable Text editors.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @portabletext/sanity-bridge
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Peer Dependencies:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @sanity/schema @sanity/types
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Convert Sanity Schema to Portable Text Schema
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import {
|
|
23
|
+
createPortableTextMemberSchemaTypes,
|
|
24
|
+
portableTextMemberSchemaTypesToSchema,
|
|
25
|
+
} from '@portabletext/sanity-bridge'
|
|
26
|
+
|
|
27
|
+
// Extract Portable Text member types from Sanity schema
|
|
28
|
+
const memberTypes = createPortableTextMemberSchemaTypes(sanityPortableTextType)
|
|
29
|
+
|
|
30
|
+
// Convert to first-class Portable Text schema
|
|
31
|
+
const portableTextSchema = portableTextMemberSchemaTypesToSchema(memberTypes)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Convert Portable Text Schema to Sanity Schema
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import {compileSchemaDefinitionToPortableTextMemberSchemaTypes} from '@portabletext/sanity-bridge'
|
|
38
|
+
|
|
39
|
+
// Convert Portable Text schema definition to Sanity types
|
|
40
|
+
const sanityMemberTypes =
|
|
41
|
+
compileSchemaDefinitionToPortableTextMemberSchemaTypes({
|
|
42
|
+
styles: [{name: 'normal'}, {name: 'h1'}],
|
|
43
|
+
decorators: [{name: 'strong'}, {name: 'em'}],
|
|
44
|
+
annotations: [{name: 'link'}],
|
|
45
|
+
blockObjects: [{name: 'image', fields: [{name: 'url', type: 'url'}]}],
|
|
46
|
+
})
|
|
47
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: !0 });
|
|
3
|
+
var schema = require("@sanity/schema"), types = require("@sanity/types"), startCase = require("lodash.startcase"), getRandomValues = require("get-random-values-esm");
|
|
4
|
+
function _interopDefaultCompat(e) {
|
|
5
|
+
return e && typeof e == "object" && "default" in e ? e : { default: e };
|
|
6
|
+
}
|
|
7
|
+
var startCase__default = /* @__PURE__ */ _interopDefaultCompat(startCase), getRandomValues__default = /* @__PURE__ */ _interopDefaultCompat(getRandomValues);
|
|
8
|
+
function createPortableTextMemberSchemaTypes(portableTextType) {
|
|
9
|
+
if (!portableTextType)
|
|
10
|
+
throw new Error("Parameter 'portabletextType' missing (required)");
|
|
11
|
+
const blockType = portableTextType.of?.find(findBlockType);
|
|
12
|
+
if (!blockType)
|
|
13
|
+
throw new Error("Block type is not defined in this schema (required)");
|
|
14
|
+
const childrenField = blockType.fields?.find(
|
|
15
|
+
(field) => field.name === "children"
|
|
16
|
+
);
|
|
17
|
+
if (!childrenField)
|
|
18
|
+
throw new Error("Children field for block type found in schema (required)");
|
|
19
|
+
const ofType = childrenField.type.of;
|
|
20
|
+
if (!ofType)
|
|
21
|
+
throw new Error(
|
|
22
|
+
"Valid types for block children not found in schema (required)"
|
|
23
|
+
);
|
|
24
|
+
const spanType = ofType.find((memberType) => memberType.name === "span");
|
|
25
|
+
if (!spanType)
|
|
26
|
+
throw new Error("Span type not found in schema (required)");
|
|
27
|
+
const inlineObjectTypes = ofType.filter(
|
|
28
|
+
(memberType) => memberType.name !== "span"
|
|
29
|
+
) || [], blockObjectTypes = portableTextType.of?.filter(
|
|
30
|
+
(field) => field.name !== blockType.name
|
|
31
|
+
) || [];
|
|
32
|
+
return {
|
|
33
|
+
styles: resolveEnabledStyles(blockType),
|
|
34
|
+
decorators: resolveEnabledDecorators(spanType),
|
|
35
|
+
lists: resolveEnabledListItems(blockType),
|
|
36
|
+
block: blockType,
|
|
37
|
+
span: spanType,
|
|
38
|
+
portableText: portableTextType,
|
|
39
|
+
inlineObjects: inlineObjectTypes,
|
|
40
|
+
blockObjects: blockObjectTypes,
|
|
41
|
+
annotations: spanType.annotations
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function resolveEnabledStyles(blockType) {
|
|
45
|
+
const styleField = blockType.fields?.find(
|
|
46
|
+
(btField) => btField.name === "style"
|
|
47
|
+
);
|
|
48
|
+
if (!styleField)
|
|
49
|
+
throw new Error(
|
|
50
|
+
"A field with name 'style' is not defined in the block type (required)."
|
|
51
|
+
);
|
|
52
|
+
const textStyles = styleField.type.options?.list && styleField.type.options.list?.filter(
|
|
53
|
+
(style) => style.value
|
|
54
|
+
);
|
|
55
|
+
if (!textStyles || textStyles.length === 0)
|
|
56
|
+
throw new Error(
|
|
57
|
+
"The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
|
|
58
|
+
);
|
|
59
|
+
return textStyles;
|
|
60
|
+
}
|
|
61
|
+
function resolveEnabledDecorators(spanType) {
|
|
62
|
+
return spanType.decorators;
|
|
63
|
+
}
|
|
64
|
+
function resolveEnabledListItems(blockType) {
|
|
65
|
+
const listField = blockType.fields?.find(
|
|
66
|
+
(btField) => btField.name === "listItem"
|
|
67
|
+
);
|
|
68
|
+
if (!listField)
|
|
69
|
+
throw new Error(
|
|
70
|
+
"A field with name 'listItem' is not defined in the block type (required)."
|
|
71
|
+
);
|
|
72
|
+
const listItems = listField.type.options?.list && listField.type.options.list.filter((list) => list.value);
|
|
73
|
+
if (!listItems)
|
|
74
|
+
throw new Error("The list field need at least to be an empty array");
|
|
75
|
+
return listItems;
|
|
76
|
+
}
|
|
77
|
+
function findBlockType(type) {
|
|
78
|
+
return type.type ? findBlockType(type.type) : type.name === "block" ? type : null;
|
|
79
|
+
}
|
|
80
|
+
function portableTextMemberSchemaTypesToSchema(schema2) {
|
|
81
|
+
return {
|
|
82
|
+
annotations: schema2.annotations.map((annotation) => ({
|
|
83
|
+
name: annotation.name,
|
|
84
|
+
fields: annotation.fields.map((field) => ({
|
|
85
|
+
name: field.name,
|
|
86
|
+
type: field.type.jsonType,
|
|
87
|
+
title: field.type.title
|
|
88
|
+
})),
|
|
89
|
+
title: annotation.title
|
|
90
|
+
})),
|
|
91
|
+
block: {
|
|
92
|
+
name: schema2.block.name
|
|
93
|
+
},
|
|
94
|
+
blockObjects: schema2.blockObjects.map((blockObject) => ({
|
|
95
|
+
name: blockObject.name,
|
|
96
|
+
fields: blockObject.fields.map((field) => ({
|
|
97
|
+
name: field.name,
|
|
98
|
+
type: field.type.jsonType,
|
|
99
|
+
title: field.type.title
|
|
100
|
+
})),
|
|
101
|
+
title: blockObject.title
|
|
102
|
+
})),
|
|
103
|
+
decorators: schema2.decorators.map((decorator) => ({
|
|
104
|
+
name: decorator.value,
|
|
105
|
+
title: decorator.title,
|
|
106
|
+
value: decorator.value
|
|
107
|
+
})),
|
|
108
|
+
inlineObjects: schema2.inlineObjects.map((inlineObject) => ({
|
|
109
|
+
name: inlineObject.name,
|
|
110
|
+
fields: inlineObject.fields.map((field) => ({
|
|
111
|
+
name: field.name,
|
|
112
|
+
type: field.type.jsonType,
|
|
113
|
+
title: field.type.title
|
|
114
|
+
})),
|
|
115
|
+
title: inlineObject.title
|
|
116
|
+
})),
|
|
117
|
+
span: {
|
|
118
|
+
name: schema2.span.name
|
|
119
|
+
},
|
|
120
|
+
styles: schema2.styles.map((style) => ({
|
|
121
|
+
name: style.value,
|
|
122
|
+
title: style.title,
|
|
123
|
+
value: style.value
|
|
124
|
+
})),
|
|
125
|
+
lists: schema2.lists.map((list) => ({
|
|
126
|
+
name: list.value,
|
|
127
|
+
title: list.title,
|
|
128
|
+
value: list.value
|
|
129
|
+
}))
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const keyGenerator = () => randomKey(12), getByteHexTable = /* @__PURE__ */ (() => {
|
|
133
|
+
let table;
|
|
134
|
+
return () => {
|
|
135
|
+
if (table)
|
|
136
|
+
return table;
|
|
137
|
+
table = [];
|
|
138
|
+
for (let i = 0; i < 256; ++i)
|
|
139
|
+
table[i] = (i + 256).toString(16).slice(1);
|
|
140
|
+
return table;
|
|
141
|
+
};
|
|
142
|
+
})();
|
|
143
|
+
function whatwgRNG(length = 16) {
|
|
144
|
+
const rnds8 = new Uint8Array(length);
|
|
145
|
+
return getRandomValues__default.default(rnds8), rnds8;
|
|
146
|
+
}
|
|
147
|
+
function randomKey(length) {
|
|
148
|
+
const table = getByteHexTable();
|
|
149
|
+
return whatwgRNG(length).reduce((str, n) => str + table[n], "").slice(0, length);
|
|
150
|
+
}
|
|
151
|
+
const temporaryImageName = `tmp-${keyGenerator()}-image`, temporaryUrlName = `tmp-${keyGenerator()}-url`, temporaryObjectNames = {
|
|
152
|
+
image: temporaryImageName,
|
|
153
|
+
url: temporaryUrlName
|
|
154
|
+
}, objectNames = {
|
|
155
|
+
[temporaryImageName]: "image",
|
|
156
|
+
[temporaryUrlName]: "url"
|
|
157
|
+
}, defaultObjectTitles = {
|
|
158
|
+
image: "Image",
|
|
159
|
+
url: "URL"
|
|
160
|
+
};
|
|
161
|
+
function compileSchemaDefinitionToPortableTextMemberSchemaTypes(definition) {
|
|
162
|
+
const blockObjects = definition?.blockObjects?.map(
|
|
163
|
+
(blockObject) => types.defineType({
|
|
164
|
+
type: "object",
|
|
165
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
166
|
+
// fields to objects with certain names.
|
|
167
|
+
name: temporaryObjectNames[blockObject.name] ?? blockObject.name,
|
|
168
|
+
title: blockObject.title === void 0 ? (
|
|
169
|
+
// This avoids the default title which is a title case of the object name
|
|
170
|
+
defaultObjectTitles[blockObject.name]
|
|
171
|
+
) : blockObject.title,
|
|
172
|
+
fields: blockObject.fields?.map((field) => ({
|
|
173
|
+
name: field.name,
|
|
174
|
+
type: field.type,
|
|
175
|
+
title: field.title ?? startCase__default.default(field.name)
|
|
176
|
+
})) ?? []
|
|
177
|
+
})
|
|
178
|
+
) ?? [], inlineObjects = definition?.inlineObjects?.map(
|
|
179
|
+
(inlineObject) => types.defineType({
|
|
180
|
+
type: "object",
|
|
181
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
182
|
+
// fields to objects with certain names.
|
|
183
|
+
name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,
|
|
184
|
+
title: inlineObject.title === void 0 ? (
|
|
185
|
+
// This avoids the default title which is a title case of the object name
|
|
186
|
+
defaultObjectTitles[inlineObject.name]
|
|
187
|
+
) : inlineObject.title,
|
|
188
|
+
fields: inlineObject.fields?.map((field) => ({
|
|
189
|
+
name: field.name,
|
|
190
|
+
type: field.type,
|
|
191
|
+
title: field.title ?? startCase__default.default(field.name)
|
|
192
|
+
})) ?? []
|
|
193
|
+
})
|
|
194
|
+
) ?? [], portableTextSchema = types.defineField({
|
|
195
|
+
type: "array",
|
|
196
|
+
name: "portable-text",
|
|
197
|
+
of: [
|
|
198
|
+
...blockObjects.map((blockObject) => ({ type: blockObject.name })),
|
|
199
|
+
{
|
|
200
|
+
type: "block",
|
|
201
|
+
name: "block",
|
|
202
|
+
of: inlineObjects.map((inlineObject) => ({ type: inlineObject.name })),
|
|
203
|
+
marks: {
|
|
204
|
+
decorators: definition?.decorators?.map((decorator) => ({
|
|
205
|
+
title: decorator.title ?? startCase__default.default(decorator.name),
|
|
206
|
+
value: decorator.name
|
|
207
|
+
})) ?? [],
|
|
208
|
+
annotations: definition?.annotations?.map((annotation) => ({
|
|
209
|
+
name: annotation.name,
|
|
210
|
+
type: "object",
|
|
211
|
+
title: annotation.title,
|
|
212
|
+
fields: annotation.fields?.map((field) => ({
|
|
213
|
+
name: field.name,
|
|
214
|
+
title: field.title ?? startCase__default.default(field.name),
|
|
215
|
+
type: field.type
|
|
216
|
+
})) ?? []
|
|
217
|
+
})) ?? []
|
|
218
|
+
},
|
|
219
|
+
lists: definition?.lists?.map((list) => ({
|
|
220
|
+
value: list.name,
|
|
221
|
+
title: list.title ?? startCase__default.default(list.name)
|
|
222
|
+
})) ?? [],
|
|
223
|
+
styles: definition?.styles?.map((style) => ({
|
|
224
|
+
value: style.name,
|
|
225
|
+
title: style.title ?? startCase__default.default(style.name)
|
|
226
|
+
})) ?? []
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}), schema$1 = schema.Schema.compile({
|
|
230
|
+
types: [portableTextSchema, ...blockObjects, ...inlineObjects]
|
|
231
|
+
}).get("portable-text"), pteSchema = createPortableTextMemberSchemaTypes(schema$1);
|
|
232
|
+
return {
|
|
233
|
+
...pteSchema,
|
|
234
|
+
blockObjects: pteSchema.blockObjects.map(
|
|
235
|
+
(blockObject) => objectNames[blockObject.name] !== void 0 ? {
|
|
236
|
+
...blockObject,
|
|
237
|
+
name: objectNames[blockObject.name],
|
|
238
|
+
type: {
|
|
239
|
+
...blockObject.type,
|
|
240
|
+
name: objectNames[blockObject.name]
|
|
241
|
+
}
|
|
242
|
+
} : blockObject
|
|
243
|
+
),
|
|
244
|
+
inlineObjects: pteSchema.inlineObjects.map(
|
|
245
|
+
(inlineObject) => objectNames[inlineObject.name] !== void 0 ? {
|
|
246
|
+
...inlineObject,
|
|
247
|
+
name: objectNames[inlineObject.name]
|
|
248
|
+
} : inlineObject
|
|
249
|
+
)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
exports.compileSchemaDefinitionToPortableTextMemberSchemaTypes = compileSchemaDefinitionToPortableTextMemberSchemaTypes;
|
|
253
|
+
exports.createPortableTextMemberSchemaTypes = createPortableTextMemberSchemaTypes;
|
|
254
|
+
exports.portableTextMemberSchemaTypesToSchema = portableTextMemberSchemaTypesToSchema;
|
|
255
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/portable-text-member-schema-types.ts","../src/portable-text-member-schema-types-to-schema.ts","../src/key-generator.ts","../src/schema-definition-to-portable-text-member-schema-types.ts"],"sourcesContent":["import type {\n ArraySchemaType,\n BlockDecoratorDefinition,\n BlockListDefinition,\n BlockSchemaType,\n BlockStyleDefinition,\n ObjectSchemaType,\n PortableTextBlock,\n SchemaType,\n SpanSchemaType,\n} from '@sanity/types'\n\n/**\n * @public\n * Sanity-specific schema types for Portable Text.\n */\nexport type PortableTextMemberSchemaTypes = {\n annotations: (ObjectSchemaType & {i18nTitleKey?: string})[]\n block: ObjectSchemaType\n blockObjects: ObjectSchemaType[]\n decorators: BlockDecoratorDefinition[]\n inlineObjects: ObjectSchemaType[]\n portableText: ArraySchemaType<PortableTextBlock>\n span: ObjectSchemaType\n styles: BlockStyleDefinition[]\n lists: BlockListDefinition[]\n}\n\n/**\n * @public\n * Create Sanity-specific schema types for Portable Text from a Sanity array\n * schema type.\n */\nexport function createPortableTextMemberSchemaTypes(\n portableTextType: ArraySchemaType<PortableTextBlock>,\n): PortableTextMemberSchemaTypes {\n if (!portableTextType) {\n throw new Error(\"Parameter 'portabletextType' missing (required)\")\n }\n const blockType = portableTextType.of?.find(findBlockType) as\n | BlockSchemaType\n | undefined\n if (!blockType) {\n throw new Error('Block type is not defined in this schema (required)')\n }\n const childrenField = blockType.fields?.find(\n (field) => field.name === 'children',\n ) as {type: ArraySchemaType} | undefined\n if (!childrenField) {\n throw new Error('Children field for block type found in schema (required)')\n }\n const ofType = childrenField.type.of\n if (!ofType) {\n throw new Error(\n 'Valid types for block children not found in schema (required)',\n )\n }\n const spanType = ofType.find((memberType) => memberType.name === 'span') as\n | ObjectSchemaType\n | undefined\n if (!spanType) {\n throw new Error('Span type not found in schema (required)')\n }\n const inlineObjectTypes = (ofType.filter(\n (memberType) => memberType.name !== 'span',\n ) || []) as ObjectSchemaType[]\n const blockObjectTypes = (portableTextType.of?.filter(\n (field) => field.name !== blockType.name,\n ) || []) as ObjectSchemaType[]\n return {\n styles: resolveEnabledStyles(blockType),\n decorators: resolveEnabledDecorators(spanType),\n lists: resolveEnabledListItems(blockType),\n block: blockType,\n span: spanType,\n portableText: portableTextType,\n inlineObjects: inlineObjectTypes,\n blockObjects: blockObjectTypes,\n annotations: (spanType as SpanSchemaType).annotations,\n }\n}\n\nfunction resolveEnabledStyles(blockType: ObjectSchemaType) {\n const styleField = blockType.fields?.find(\n (btField) => btField.name === 'style',\n )\n if (!styleField) {\n throw new Error(\n \"A field with name 'style' is not defined in the block type (required).\",\n )\n }\n const textStyles =\n styleField.type.options?.list &&\n styleField.type.options.list?.filter(\n (style: {value: string}) => style.value,\n )\n if (!textStyles || textStyles.length === 0) {\n throw new Error(\n 'The style fields need at least one style ' +\n \"defined. I.e: {title: 'Normal', value: 'normal'}.\",\n )\n }\n return textStyles\n}\n\nfunction resolveEnabledDecorators(spanType: ObjectSchemaType) {\n return (spanType as any).decorators\n}\n\nfunction resolveEnabledListItems(blockType: ObjectSchemaType) {\n const listField = blockType.fields?.find(\n (btField) => btField.name === 'listItem',\n )\n if (!listField) {\n throw new Error(\n \"A field with name 'listItem' is not defined in the block type (required).\",\n )\n }\n const listItems =\n listField.type.options?.list &&\n listField.type.options.list.filter((list: {value: string}) => list.value)\n if (!listItems) {\n throw new Error('The list field need at least to be an empty array')\n }\n return listItems\n}\n\nfunction findBlockType(type: SchemaType): BlockSchemaType | null {\n if (type.type) {\n return findBlockType(type.type)\n }\n\n if (type.name === 'block') {\n return type as BlockSchemaType\n }\n\n return null\n}\n","import type {Schema} from '@portabletext/schema'\nimport type {PortableTextMemberSchemaTypes} from './portable-text-member-schema-types'\n\n/**\n * @public\n * Convert Sanity-specific schema types for Portable Text to a first-class\n * Portable Text schema.\n */\nexport function portableTextMemberSchemaTypesToSchema(\n schema: PortableTextMemberSchemaTypes,\n): Schema {\n return {\n annotations: schema.annotations.map((annotation) => ({\n name: annotation.name,\n fields: annotation.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: annotation.title,\n })),\n block: {\n name: schema.block.name,\n },\n blockObjects: schema.blockObjects.map((blockObject) => ({\n name: blockObject.name,\n fields: blockObject.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: blockObject.title,\n })),\n decorators: schema.decorators.map((decorator) => ({\n name: decorator.value,\n title: decorator.title,\n value: decorator.value,\n })),\n inlineObjects: schema.inlineObjects.map((inlineObject) => ({\n name: inlineObject.name,\n fields: inlineObject.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: inlineObject.title,\n })),\n span: {\n name: schema.span.name,\n },\n styles: schema.styles.map((style) => ({\n name: style.value,\n title: style.title,\n value: style.value,\n })),\n lists: schema.lists.map((list) => ({\n name: list.value,\n title: list.title,\n value: list.value,\n })),\n }\n}\n","import getRandomValues from 'get-random-values-esm'\n\nexport const keyGenerator = (): string => randomKey(12)\n\nconst getByteHexTable = (() => {\n let table: any[]\n return () => {\n if (table) {\n return table\n }\n\n table = []\n for (let i = 0; i < 256; ++i) {\n table[i] = (i + 0x100).toString(16).slice(1)\n }\n return table\n }\n})()\n\n// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html\nfunction whatwgRNG(length = 16) {\n const rnds8 = new Uint8Array(length)\n getRandomValues(rnds8)\n return rnds8\n}\n\nfunction randomKey(length?: number): string {\n const table = getByteHexTable()\n return whatwgRNG(length)\n .reduce((str, n) => str + table[n], '')\n .slice(0, length)\n}\n","import type {SchemaDefinition} from '@portabletext/schema'\nimport {Schema as SanitySchema} from '@sanity/schema'\nimport {defineField, defineType, type ObjectSchemaType} from '@sanity/types'\nimport startCase from 'lodash.startcase'\nimport {keyGenerator} from './key-generator'\nimport {\n createPortableTextMemberSchemaTypes,\n type PortableTextMemberSchemaTypes,\n} from './portable-text-member-schema-types'\n\nconst temporaryImageName = `tmp-${keyGenerator()}-image`\nconst temporaryUrlName = `tmp-${keyGenerator()}-url`\n\nconst temporaryObjectNames: Record<string, string> = {\n image: temporaryImageName,\n url: temporaryUrlName,\n}\n\nconst objectNames: Record<string, string> = {\n [temporaryImageName]: 'image',\n [temporaryUrlName]: 'url',\n}\n\nconst defaultObjectTitles: Record<string, string> = {\n image: 'Image',\n url: 'URL',\n}\n\n/**\n * @public\n * Compile a Portable Text schema definition to Sanity-specific schema types for\n * Portable Text.\n */\nexport function compileSchemaDefinitionToPortableTextMemberSchemaTypes(\n definition?: SchemaDefinition,\n): PortableTextMemberSchemaTypes {\n const blockObjects =\n definition?.blockObjects?.map((blockObject) =>\n defineType({\n type: 'object',\n // Very naive way to work around `SanitySchema.compile` adding default\n // fields to objects with certain names.\n name: temporaryObjectNames[blockObject.name] ?? blockObject.name,\n title:\n blockObject.title === undefined\n ? // This avoids the default title which is a title case of the object name\n defaultObjectTitles[blockObject.name]\n : blockObject.title,\n fields:\n blockObject.fields?.map((field) => ({\n name: field.name,\n type: field.type,\n title: field.title ?? startCase(field.name),\n })) ?? [],\n }),\n ) ?? []\n\n const inlineObjects =\n definition?.inlineObjects?.map((inlineObject) =>\n defineType({\n type: 'object',\n // Very naive way to work around `SanitySchema.compile` adding default\n // fields to objects with certain names.\n name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,\n\n title:\n inlineObject.title === undefined\n ? // This avoids the default title which is a title case of the object name\n defaultObjectTitles[inlineObject.name]\n : inlineObject.title,\n fields:\n inlineObject.fields?.map((field) => ({\n name: field.name,\n type: field.type,\n title: field.title ?? startCase(field.name),\n })) ?? [],\n }),\n ) ?? []\n\n const portableTextSchema = defineField({\n type: 'array',\n name: 'portable-text',\n of: [\n ...blockObjects.map((blockObject) => ({type: blockObject.name})),\n {\n type: 'block',\n name: 'block',\n of: inlineObjects.map((inlineObject) => ({type: inlineObject.name})),\n marks: {\n decorators:\n definition?.decorators?.map((decorator) => ({\n title: decorator.title ?? startCase(decorator.name),\n value: decorator.name,\n })) ?? [],\n annotations:\n definition?.annotations?.map((annotation) => ({\n name: annotation.name,\n type: 'object',\n title: annotation.title,\n fields:\n annotation.fields?.map((field) => ({\n name: field.name,\n title: field.title ?? startCase(field.name),\n type: field.type,\n })) ?? [],\n })) ?? [],\n },\n lists:\n definition?.lists?.map((list) => ({\n value: list.name,\n title: list.title ?? startCase(list.name),\n })) ?? [],\n styles:\n definition?.styles?.map((style) => ({\n value: style.name,\n title: style.title ?? startCase(style.name),\n })) ?? [],\n },\n ],\n })\n\n const schema = SanitySchema.compile({\n types: [portableTextSchema, ...blockObjects, ...inlineObjects],\n }).get('portable-text')\n\n const pteSchema = createPortableTextMemberSchemaTypes(schema)\n\n return {\n ...pteSchema,\n blockObjects: pteSchema.blockObjects.map((blockObject) =>\n objectNames[blockObject.name] !== undefined\n ? ({\n ...blockObject,\n name: objectNames[blockObject.name],\n type: {\n ...blockObject.type,\n name: objectNames[blockObject.name],\n },\n } as ObjectSchemaType)\n : blockObject,\n ),\n inlineObjects: pteSchema.inlineObjects.map((inlineObject) =>\n objectNames[inlineObject.name] !== undefined\n ? ({\n ...inlineObject,\n name: objectNames[inlineObject.name],\n } as ObjectSchemaType)\n : inlineObject,\n ),\n } satisfies PortableTextMemberSchemaTypes\n}\n"],"names":["schema","getRandomValues","defineType","startCase","defineField","SanitySchema"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ArraySchemaType, BlockDecoratorDefinition, BlockListDefinition, BlockStyleDefinition, ObjectSchemaType, PortableTextBlock } from "@sanity/types";
|
|
2
|
+
import { Schema, SchemaDefinition } from "@portabletext/schema";
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* Sanity-specific schema types for Portable Text.
|
|
6
|
+
*/
|
|
7
|
+
type PortableTextMemberSchemaTypes = {
|
|
8
|
+
annotations: (ObjectSchemaType & {
|
|
9
|
+
i18nTitleKey?: string;
|
|
10
|
+
})[];
|
|
11
|
+
block: ObjectSchemaType;
|
|
12
|
+
blockObjects: ObjectSchemaType[];
|
|
13
|
+
decorators: BlockDecoratorDefinition[];
|
|
14
|
+
inlineObjects: ObjectSchemaType[];
|
|
15
|
+
portableText: ArraySchemaType<PortableTextBlock>;
|
|
16
|
+
span: ObjectSchemaType;
|
|
17
|
+
styles: BlockStyleDefinition[];
|
|
18
|
+
lists: BlockListDefinition[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
* Create Sanity-specific schema types for Portable Text from a Sanity array
|
|
23
|
+
* schema type.
|
|
24
|
+
*/
|
|
25
|
+
declare function createPortableTextMemberSchemaTypes(portableTextType: ArraySchemaType<PortableTextBlock>): PortableTextMemberSchemaTypes;
|
|
26
|
+
/**
|
|
27
|
+
* @public
|
|
28
|
+
* Convert Sanity-specific schema types for Portable Text to a first-class
|
|
29
|
+
* Portable Text schema.
|
|
30
|
+
*/
|
|
31
|
+
declare function portableTextMemberSchemaTypesToSchema(schema: PortableTextMemberSchemaTypes): Schema;
|
|
32
|
+
/**
|
|
33
|
+
* @public
|
|
34
|
+
* Compile a Portable Text schema definition to Sanity-specific schema types for
|
|
35
|
+
* Portable Text.
|
|
36
|
+
*/
|
|
37
|
+
declare function compileSchemaDefinitionToPortableTextMemberSchemaTypes(definition?: SchemaDefinition): PortableTextMemberSchemaTypes;
|
|
38
|
+
export { type PortableTextMemberSchemaTypes, compileSchemaDefinitionToPortableTextMemberSchemaTypes, createPortableTextMemberSchemaTypes, portableTextMemberSchemaTypesToSchema };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ArraySchemaType, BlockDecoratorDefinition, BlockListDefinition, BlockStyleDefinition, ObjectSchemaType, PortableTextBlock } from "@sanity/types";
|
|
2
|
+
import { Schema, SchemaDefinition } from "@portabletext/schema";
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* Sanity-specific schema types for Portable Text.
|
|
6
|
+
*/
|
|
7
|
+
type PortableTextMemberSchemaTypes = {
|
|
8
|
+
annotations: (ObjectSchemaType & {
|
|
9
|
+
i18nTitleKey?: string;
|
|
10
|
+
})[];
|
|
11
|
+
block: ObjectSchemaType;
|
|
12
|
+
blockObjects: ObjectSchemaType[];
|
|
13
|
+
decorators: BlockDecoratorDefinition[];
|
|
14
|
+
inlineObjects: ObjectSchemaType[];
|
|
15
|
+
portableText: ArraySchemaType<PortableTextBlock>;
|
|
16
|
+
span: ObjectSchemaType;
|
|
17
|
+
styles: BlockStyleDefinition[];
|
|
18
|
+
lists: BlockListDefinition[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
* Create Sanity-specific schema types for Portable Text from a Sanity array
|
|
23
|
+
* schema type.
|
|
24
|
+
*/
|
|
25
|
+
declare function createPortableTextMemberSchemaTypes(portableTextType: ArraySchemaType<PortableTextBlock>): PortableTextMemberSchemaTypes;
|
|
26
|
+
/**
|
|
27
|
+
* @public
|
|
28
|
+
* Convert Sanity-specific schema types for Portable Text to a first-class
|
|
29
|
+
* Portable Text schema.
|
|
30
|
+
*/
|
|
31
|
+
declare function portableTextMemberSchemaTypesToSchema(schema: PortableTextMemberSchemaTypes): Schema;
|
|
32
|
+
/**
|
|
33
|
+
* @public
|
|
34
|
+
* Compile a Portable Text schema definition to Sanity-specific schema types for
|
|
35
|
+
* Portable Text.
|
|
36
|
+
*/
|
|
37
|
+
declare function compileSchemaDefinitionToPortableTextMemberSchemaTypes(definition?: SchemaDefinition): PortableTextMemberSchemaTypes;
|
|
38
|
+
export { type PortableTextMemberSchemaTypes, compileSchemaDefinitionToPortableTextMemberSchemaTypes, createPortableTextMemberSchemaTypes, portableTextMemberSchemaTypesToSchema };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Schema } from "@sanity/schema";
|
|
2
|
+
import { defineType, defineField } from "@sanity/types";
|
|
3
|
+
import startCase from "lodash.startcase";
|
|
4
|
+
import getRandomValues from "get-random-values-esm";
|
|
5
|
+
function createPortableTextMemberSchemaTypes(portableTextType) {
|
|
6
|
+
if (!portableTextType)
|
|
7
|
+
throw new Error("Parameter 'portabletextType' missing (required)");
|
|
8
|
+
const blockType = portableTextType.of?.find(findBlockType);
|
|
9
|
+
if (!blockType)
|
|
10
|
+
throw new Error("Block type is not defined in this schema (required)");
|
|
11
|
+
const childrenField = blockType.fields?.find(
|
|
12
|
+
(field) => field.name === "children"
|
|
13
|
+
);
|
|
14
|
+
if (!childrenField)
|
|
15
|
+
throw new Error("Children field for block type found in schema (required)");
|
|
16
|
+
const ofType = childrenField.type.of;
|
|
17
|
+
if (!ofType)
|
|
18
|
+
throw new Error(
|
|
19
|
+
"Valid types for block children not found in schema (required)"
|
|
20
|
+
);
|
|
21
|
+
const spanType = ofType.find((memberType) => memberType.name === "span");
|
|
22
|
+
if (!spanType)
|
|
23
|
+
throw new Error("Span type not found in schema (required)");
|
|
24
|
+
const inlineObjectTypes = ofType.filter(
|
|
25
|
+
(memberType) => memberType.name !== "span"
|
|
26
|
+
) || [], blockObjectTypes = portableTextType.of?.filter(
|
|
27
|
+
(field) => field.name !== blockType.name
|
|
28
|
+
) || [];
|
|
29
|
+
return {
|
|
30
|
+
styles: resolveEnabledStyles(blockType),
|
|
31
|
+
decorators: resolveEnabledDecorators(spanType),
|
|
32
|
+
lists: resolveEnabledListItems(blockType),
|
|
33
|
+
block: blockType,
|
|
34
|
+
span: spanType,
|
|
35
|
+
portableText: portableTextType,
|
|
36
|
+
inlineObjects: inlineObjectTypes,
|
|
37
|
+
blockObjects: blockObjectTypes,
|
|
38
|
+
annotations: spanType.annotations
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function resolveEnabledStyles(blockType) {
|
|
42
|
+
const styleField = blockType.fields?.find(
|
|
43
|
+
(btField) => btField.name === "style"
|
|
44
|
+
);
|
|
45
|
+
if (!styleField)
|
|
46
|
+
throw new Error(
|
|
47
|
+
"A field with name 'style' is not defined in the block type (required)."
|
|
48
|
+
);
|
|
49
|
+
const textStyles = styleField.type.options?.list && styleField.type.options.list?.filter(
|
|
50
|
+
(style) => style.value
|
|
51
|
+
);
|
|
52
|
+
if (!textStyles || textStyles.length === 0)
|
|
53
|
+
throw new Error(
|
|
54
|
+
"The style fields need at least one style defined. I.e: {title: 'Normal', value: 'normal'}."
|
|
55
|
+
);
|
|
56
|
+
return textStyles;
|
|
57
|
+
}
|
|
58
|
+
function resolveEnabledDecorators(spanType) {
|
|
59
|
+
return spanType.decorators;
|
|
60
|
+
}
|
|
61
|
+
function resolveEnabledListItems(blockType) {
|
|
62
|
+
const listField = blockType.fields?.find(
|
|
63
|
+
(btField) => btField.name === "listItem"
|
|
64
|
+
);
|
|
65
|
+
if (!listField)
|
|
66
|
+
throw new Error(
|
|
67
|
+
"A field with name 'listItem' is not defined in the block type (required)."
|
|
68
|
+
);
|
|
69
|
+
const listItems = listField.type.options?.list && listField.type.options.list.filter((list) => list.value);
|
|
70
|
+
if (!listItems)
|
|
71
|
+
throw new Error("The list field need at least to be an empty array");
|
|
72
|
+
return listItems;
|
|
73
|
+
}
|
|
74
|
+
function findBlockType(type) {
|
|
75
|
+
return type.type ? findBlockType(type.type) : type.name === "block" ? type : null;
|
|
76
|
+
}
|
|
77
|
+
function portableTextMemberSchemaTypesToSchema(schema) {
|
|
78
|
+
return {
|
|
79
|
+
annotations: schema.annotations.map((annotation) => ({
|
|
80
|
+
name: annotation.name,
|
|
81
|
+
fields: annotation.fields.map((field) => ({
|
|
82
|
+
name: field.name,
|
|
83
|
+
type: field.type.jsonType,
|
|
84
|
+
title: field.type.title
|
|
85
|
+
})),
|
|
86
|
+
title: annotation.title
|
|
87
|
+
})),
|
|
88
|
+
block: {
|
|
89
|
+
name: schema.block.name
|
|
90
|
+
},
|
|
91
|
+
blockObjects: schema.blockObjects.map((blockObject) => ({
|
|
92
|
+
name: blockObject.name,
|
|
93
|
+
fields: blockObject.fields.map((field) => ({
|
|
94
|
+
name: field.name,
|
|
95
|
+
type: field.type.jsonType,
|
|
96
|
+
title: field.type.title
|
|
97
|
+
})),
|
|
98
|
+
title: blockObject.title
|
|
99
|
+
})),
|
|
100
|
+
decorators: schema.decorators.map((decorator) => ({
|
|
101
|
+
name: decorator.value,
|
|
102
|
+
title: decorator.title,
|
|
103
|
+
value: decorator.value
|
|
104
|
+
})),
|
|
105
|
+
inlineObjects: schema.inlineObjects.map((inlineObject) => ({
|
|
106
|
+
name: inlineObject.name,
|
|
107
|
+
fields: inlineObject.fields.map((field) => ({
|
|
108
|
+
name: field.name,
|
|
109
|
+
type: field.type.jsonType,
|
|
110
|
+
title: field.type.title
|
|
111
|
+
})),
|
|
112
|
+
title: inlineObject.title
|
|
113
|
+
})),
|
|
114
|
+
span: {
|
|
115
|
+
name: schema.span.name
|
|
116
|
+
},
|
|
117
|
+
styles: schema.styles.map((style) => ({
|
|
118
|
+
name: style.value,
|
|
119
|
+
title: style.title,
|
|
120
|
+
value: style.value
|
|
121
|
+
})),
|
|
122
|
+
lists: schema.lists.map((list) => ({
|
|
123
|
+
name: list.value,
|
|
124
|
+
title: list.title,
|
|
125
|
+
value: list.value
|
|
126
|
+
}))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const keyGenerator = () => randomKey(12), getByteHexTable = /* @__PURE__ */ (() => {
|
|
130
|
+
let table;
|
|
131
|
+
return () => {
|
|
132
|
+
if (table)
|
|
133
|
+
return table;
|
|
134
|
+
table = [];
|
|
135
|
+
for (let i = 0; i < 256; ++i)
|
|
136
|
+
table[i] = (i + 256).toString(16).slice(1);
|
|
137
|
+
return table;
|
|
138
|
+
};
|
|
139
|
+
})();
|
|
140
|
+
function whatwgRNG(length = 16) {
|
|
141
|
+
const rnds8 = new Uint8Array(length);
|
|
142
|
+
return getRandomValues(rnds8), rnds8;
|
|
143
|
+
}
|
|
144
|
+
function randomKey(length) {
|
|
145
|
+
const table = getByteHexTable();
|
|
146
|
+
return whatwgRNG(length).reduce((str, n) => str + table[n], "").slice(0, length);
|
|
147
|
+
}
|
|
148
|
+
const temporaryImageName = `tmp-${keyGenerator()}-image`, temporaryUrlName = `tmp-${keyGenerator()}-url`, temporaryObjectNames = {
|
|
149
|
+
image: temporaryImageName,
|
|
150
|
+
url: temporaryUrlName
|
|
151
|
+
}, objectNames = {
|
|
152
|
+
[temporaryImageName]: "image",
|
|
153
|
+
[temporaryUrlName]: "url"
|
|
154
|
+
}, defaultObjectTitles = {
|
|
155
|
+
image: "Image",
|
|
156
|
+
url: "URL"
|
|
157
|
+
};
|
|
158
|
+
function compileSchemaDefinitionToPortableTextMemberSchemaTypes(definition) {
|
|
159
|
+
const blockObjects = definition?.blockObjects?.map(
|
|
160
|
+
(blockObject) => defineType({
|
|
161
|
+
type: "object",
|
|
162
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
163
|
+
// fields to objects with certain names.
|
|
164
|
+
name: temporaryObjectNames[blockObject.name] ?? blockObject.name,
|
|
165
|
+
title: blockObject.title === void 0 ? (
|
|
166
|
+
// This avoids the default title which is a title case of the object name
|
|
167
|
+
defaultObjectTitles[blockObject.name]
|
|
168
|
+
) : blockObject.title,
|
|
169
|
+
fields: blockObject.fields?.map((field) => ({
|
|
170
|
+
name: field.name,
|
|
171
|
+
type: field.type,
|
|
172
|
+
title: field.title ?? startCase(field.name)
|
|
173
|
+
})) ?? []
|
|
174
|
+
})
|
|
175
|
+
) ?? [], inlineObjects = definition?.inlineObjects?.map(
|
|
176
|
+
(inlineObject) => defineType({
|
|
177
|
+
type: "object",
|
|
178
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
179
|
+
// fields to objects with certain names.
|
|
180
|
+
name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,
|
|
181
|
+
title: inlineObject.title === void 0 ? (
|
|
182
|
+
// This avoids the default title which is a title case of the object name
|
|
183
|
+
defaultObjectTitles[inlineObject.name]
|
|
184
|
+
) : inlineObject.title,
|
|
185
|
+
fields: inlineObject.fields?.map((field) => ({
|
|
186
|
+
name: field.name,
|
|
187
|
+
type: field.type,
|
|
188
|
+
title: field.title ?? startCase(field.name)
|
|
189
|
+
})) ?? []
|
|
190
|
+
})
|
|
191
|
+
) ?? [], portableTextSchema = defineField({
|
|
192
|
+
type: "array",
|
|
193
|
+
name: "portable-text",
|
|
194
|
+
of: [
|
|
195
|
+
...blockObjects.map((blockObject) => ({ type: blockObject.name })),
|
|
196
|
+
{
|
|
197
|
+
type: "block",
|
|
198
|
+
name: "block",
|
|
199
|
+
of: inlineObjects.map((inlineObject) => ({ type: inlineObject.name })),
|
|
200
|
+
marks: {
|
|
201
|
+
decorators: definition?.decorators?.map((decorator) => ({
|
|
202
|
+
title: decorator.title ?? startCase(decorator.name),
|
|
203
|
+
value: decorator.name
|
|
204
|
+
})) ?? [],
|
|
205
|
+
annotations: definition?.annotations?.map((annotation) => ({
|
|
206
|
+
name: annotation.name,
|
|
207
|
+
type: "object",
|
|
208
|
+
title: annotation.title,
|
|
209
|
+
fields: annotation.fields?.map((field) => ({
|
|
210
|
+
name: field.name,
|
|
211
|
+
title: field.title ?? startCase(field.name),
|
|
212
|
+
type: field.type
|
|
213
|
+
})) ?? []
|
|
214
|
+
})) ?? []
|
|
215
|
+
},
|
|
216
|
+
lists: definition?.lists?.map((list) => ({
|
|
217
|
+
value: list.name,
|
|
218
|
+
title: list.title ?? startCase(list.name)
|
|
219
|
+
})) ?? [],
|
|
220
|
+
styles: definition?.styles?.map((style) => ({
|
|
221
|
+
value: style.name,
|
|
222
|
+
title: style.title ?? startCase(style.name)
|
|
223
|
+
})) ?? []
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
}), schema = Schema.compile({
|
|
227
|
+
types: [portableTextSchema, ...blockObjects, ...inlineObjects]
|
|
228
|
+
}).get("portable-text"), pteSchema = createPortableTextMemberSchemaTypes(schema);
|
|
229
|
+
return {
|
|
230
|
+
...pteSchema,
|
|
231
|
+
blockObjects: pteSchema.blockObjects.map(
|
|
232
|
+
(blockObject) => objectNames[blockObject.name] !== void 0 ? {
|
|
233
|
+
...blockObject,
|
|
234
|
+
name: objectNames[blockObject.name],
|
|
235
|
+
type: {
|
|
236
|
+
...blockObject.type,
|
|
237
|
+
name: objectNames[blockObject.name]
|
|
238
|
+
}
|
|
239
|
+
} : blockObject
|
|
240
|
+
),
|
|
241
|
+
inlineObjects: pteSchema.inlineObjects.map(
|
|
242
|
+
(inlineObject) => objectNames[inlineObject.name] !== void 0 ? {
|
|
243
|
+
...inlineObject,
|
|
244
|
+
name: objectNames[inlineObject.name]
|
|
245
|
+
} : inlineObject
|
|
246
|
+
)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export {
|
|
250
|
+
compileSchemaDefinitionToPortableTextMemberSchemaTypes,
|
|
251
|
+
createPortableTextMemberSchemaTypes,
|
|
252
|
+
portableTextMemberSchemaTypesToSchema
|
|
253
|
+
};
|
|
254
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/portable-text-member-schema-types.ts","../src/portable-text-member-schema-types-to-schema.ts","../src/key-generator.ts","../src/schema-definition-to-portable-text-member-schema-types.ts"],"sourcesContent":["import type {\n ArraySchemaType,\n BlockDecoratorDefinition,\n BlockListDefinition,\n BlockSchemaType,\n BlockStyleDefinition,\n ObjectSchemaType,\n PortableTextBlock,\n SchemaType,\n SpanSchemaType,\n} from '@sanity/types'\n\n/**\n * @public\n * Sanity-specific schema types for Portable Text.\n */\nexport type PortableTextMemberSchemaTypes = {\n annotations: (ObjectSchemaType & {i18nTitleKey?: string})[]\n block: ObjectSchemaType\n blockObjects: ObjectSchemaType[]\n decorators: BlockDecoratorDefinition[]\n inlineObjects: ObjectSchemaType[]\n portableText: ArraySchemaType<PortableTextBlock>\n span: ObjectSchemaType\n styles: BlockStyleDefinition[]\n lists: BlockListDefinition[]\n}\n\n/**\n * @public\n * Create Sanity-specific schema types for Portable Text from a Sanity array\n * schema type.\n */\nexport function createPortableTextMemberSchemaTypes(\n portableTextType: ArraySchemaType<PortableTextBlock>,\n): PortableTextMemberSchemaTypes {\n if (!portableTextType) {\n throw new Error(\"Parameter 'portabletextType' missing (required)\")\n }\n const blockType = portableTextType.of?.find(findBlockType) as\n | BlockSchemaType\n | undefined\n if (!blockType) {\n throw new Error('Block type is not defined in this schema (required)')\n }\n const childrenField = blockType.fields?.find(\n (field) => field.name === 'children',\n ) as {type: ArraySchemaType} | undefined\n if (!childrenField) {\n throw new Error('Children field for block type found in schema (required)')\n }\n const ofType = childrenField.type.of\n if (!ofType) {\n throw new Error(\n 'Valid types for block children not found in schema (required)',\n )\n }\n const spanType = ofType.find((memberType) => memberType.name === 'span') as\n | ObjectSchemaType\n | undefined\n if (!spanType) {\n throw new Error('Span type not found in schema (required)')\n }\n const inlineObjectTypes = (ofType.filter(\n (memberType) => memberType.name !== 'span',\n ) || []) as ObjectSchemaType[]\n const blockObjectTypes = (portableTextType.of?.filter(\n (field) => field.name !== blockType.name,\n ) || []) as ObjectSchemaType[]\n return {\n styles: resolveEnabledStyles(blockType),\n decorators: resolveEnabledDecorators(spanType),\n lists: resolveEnabledListItems(blockType),\n block: blockType,\n span: spanType,\n portableText: portableTextType,\n inlineObjects: inlineObjectTypes,\n blockObjects: blockObjectTypes,\n annotations: (spanType as SpanSchemaType).annotations,\n }\n}\n\nfunction resolveEnabledStyles(blockType: ObjectSchemaType) {\n const styleField = blockType.fields?.find(\n (btField) => btField.name === 'style',\n )\n if (!styleField) {\n throw new Error(\n \"A field with name 'style' is not defined in the block type (required).\",\n )\n }\n const textStyles =\n styleField.type.options?.list &&\n styleField.type.options.list?.filter(\n (style: {value: string}) => style.value,\n )\n if (!textStyles || textStyles.length === 0) {\n throw new Error(\n 'The style fields need at least one style ' +\n \"defined. I.e: {title: 'Normal', value: 'normal'}.\",\n )\n }\n return textStyles\n}\n\nfunction resolveEnabledDecorators(spanType: ObjectSchemaType) {\n return (spanType as any).decorators\n}\n\nfunction resolveEnabledListItems(blockType: ObjectSchemaType) {\n const listField = blockType.fields?.find(\n (btField) => btField.name === 'listItem',\n )\n if (!listField) {\n throw new Error(\n \"A field with name 'listItem' is not defined in the block type (required).\",\n )\n }\n const listItems =\n listField.type.options?.list &&\n listField.type.options.list.filter((list: {value: string}) => list.value)\n if (!listItems) {\n throw new Error('The list field need at least to be an empty array')\n }\n return listItems\n}\n\nfunction findBlockType(type: SchemaType): BlockSchemaType | null {\n if (type.type) {\n return findBlockType(type.type)\n }\n\n if (type.name === 'block') {\n return type as BlockSchemaType\n }\n\n return null\n}\n","import type {Schema} from '@portabletext/schema'\nimport type {PortableTextMemberSchemaTypes} from './portable-text-member-schema-types'\n\n/**\n * @public\n * Convert Sanity-specific schema types for Portable Text to a first-class\n * Portable Text schema.\n */\nexport function portableTextMemberSchemaTypesToSchema(\n schema: PortableTextMemberSchemaTypes,\n): Schema {\n return {\n annotations: schema.annotations.map((annotation) => ({\n name: annotation.name,\n fields: annotation.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: annotation.title,\n })),\n block: {\n name: schema.block.name,\n },\n blockObjects: schema.blockObjects.map((blockObject) => ({\n name: blockObject.name,\n fields: blockObject.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: blockObject.title,\n })),\n decorators: schema.decorators.map((decorator) => ({\n name: decorator.value,\n title: decorator.title,\n value: decorator.value,\n })),\n inlineObjects: schema.inlineObjects.map((inlineObject) => ({\n name: inlineObject.name,\n fields: inlineObject.fields.map((field) => ({\n name: field.name,\n type: field.type.jsonType,\n title: field.type.title,\n })),\n title: inlineObject.title,\n })),\n span: {\n name: schema.span.name,\n },\n styles: schema.styles.map((style) => ({\n name: style.value,\n title: style.title,\n value: style.value,\n })),\n lists: schema.lists.map((list) => ({\n name: list.value,\n title: list.title,\n value: list.value,\n })),\n }\n}\n","import getRandomValues from 'get-random-values-esm'\n\nexport const keyGenerator = (): string => randomKey(12)\n\nconst getByteHexTable = (() => {\n let table: any[]\n return () => {\n if (table) {\n return table\n }\n\n table = []\n for (let i = 0; i < 256; ++i) {\n table[i] = (i + 0x100).toString(16).slice(1)\n }\n return table\n }\n})()\n\n// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html\nfunction whatwgRNG(length = 16) {\n const rnds8 = new Uint8Array(length)\n getRandomValues(rnds8)\n return rnds8\n}\n\nfunction randomKey(length?: number): string {\n const table = getByteHexTable()\n return whatwgRNG(length)\n .reduce((str, n) => str + table[n], '')\n .slice(0, length)\n}\n","import type {SchemaDefinition} from '@portabletext/schema'\nimport {Schema as SanitySchema} from '@sanity/schema'\nimport {defineField, defineType, type ObjectSchemaType} from '@sanity/types'\nimport startCase from 'lodash.startcase'\nimport {keyGenerator} from './key-generator'\nimport {\n createPortableTextMemberSchemaTypes,\n type PortableTextMemberSchemaTypes,\n} from './portable-text-member-schema-types'\n\nconst temporaryImageName = `tmp-${keyGenerator()}-image`\nconst temporaryUrlName = `tmp-${keyGenerator()}-url`\n\nconst temporaryObjectNames: Record<string, string> = {\n image: temporaryImageName,\n url: temporaryUrlName,\n}\n\nconst objectNames: Record<string, string> = {\n [temporaryImageName]: 'image',\n [temporaryUrlName]: 'url',\n}\n\nconst defaultObjectTitles: Record<string, string> = {\n image: 'Image',\n url: 'URL',\n}\n\n/**\n * @public\n * Compile a Portable Text schema definition to Sanity-specific schema types for\n * Portable Text.\n */\nexport function compileSchemaDefinitionToPortableTextMemberSchemaTypes(\n definition?: SchemaDefinition,\n): PortableTextMemberSchemaTypes {\n const blockObjects =\n definition?.blockObjects?.map((blockObject) =>\n defineType({\n type: 'object',\n // Very naive way to work around `SanitySchema.compile` adding default\n // fields to objects with certain names.\n name: temporaryObjectNames[blockObject.name] ?? blockObject.name,\n title:\n blockObject.title === undefined\n ? // This avoids the default title which is a title case of the object name\n defaultObjectTitles[blockObject.name]\n : blockObject.title,\n fields:\n blockObject.fields?.map((field) => ({\n name: field.name,\n type: field.type,\n title: field.title ?? startCase(field.name),\n })) ?? [],\n }),\n ) ?? []\n\n const inlineObjects =\n definition?.inlineObjects?.map((inlineObject) =>\n defineType({\n type: 'object',\n // Very naive way to work around `SanitySchema.compile` adding default\n // fields to objects with certain names.\n name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,\n\n title:\n inlineObject.title === undefined\n ? // This avoids the default title which is a title case of the object name\n defaultObjectTitles[inlineObject.name]\n : inlineObject.title,\n fields:\n inlineObject.fields?.map((field) => ({\n name: field.name,\n type: field.type,\n title: field.title ?? startCase(field.name),\n })) ?? [],\n }),\n ) ?? []\n\n const portableTextSchema = defineField({\n type: 'array',\n name: 'portable-text',\n of: [\n ...blockObjects.map((blockObject) => ({type: blockObject.name})),\n {\n type: 'block',\n name: 'block',\n of: inlineObjects.map((inlineObject) => ({type: inlineObject.name})),\n marks: {\n decorators:\n definition?.decorators?.map((decorator) => ({\n title: decorator.title ?? startCase(decorator.name),\n value: decorator.name,\n })) ?? [],\n annotations:\n definition?.annotations?.map((annotation) => ({\n name: annotation.name,\n type: 'object',\n title: annotation.title,\n fields:\n annotation.fields?.map((field) => ({\n name: field.name,\n title: field.title ?? startCase(field.name),\n type: field.type,\n })) ?? [],\n })) ?? [],\n },\n lists:\n definition?.lists?.map((list) => ({\n value: list.name,\n title: list.title ?? startCase(list.name),\n })) ?? [],\n styles:\n definition?.styles?.map((style) => ({\n value: style.name,\n title: style.title ?? startCase(style.name),\n })) ?? [],\n },\n ],\n })\n\n const schema = SanitySchema.compile({\n types: [portableTextSchema, ...blockObjects, ...inlineObjects],\n }).get('portable-text')\n\n const pteSchema = createPortableTextMemberSchemaTypes(schema)\n\n return {\n ...pteSchema,\n blockObjects: pteSchema.blockObjects.map((blockObject) =>\n objectNames[blockObject.name] !== undefined\n ? ({\n ...blockObject,\n name: objectNames[blockObject.name],\n type: {\n ...blockObject.type,\n name: objectNames[blockObject.name],\n },\n } as ObjectSchemaType)\n : blockObject,\n ),\n inlineObjects: pteSchema.inlineObjects.map((inlineObject) =>\n objectNames[inlineObject.name] !== undefined\n ? ({\n ...inlineObject,\n name: objectNames[inlineObject.name],\n } as ObjectSchemaType)\n : inlineObject,\n ),\n } satisfies PortableTextMemberSchemaTypes\n}\n"],"names":["SanitySchema"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@portabletext/sanity-bridge",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Convert a Sanity Schema to a Portable Text Schema",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sanity",
|
|
7
|
+
"portable-text"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://www.portabletext.org/",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/portabletext/editor/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/portabletext/editor.git",
|
|
16
|
+
"directory": "packages/sanity-bridge"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Sanity.io <hello@sanity.io>",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"source": "./src/index.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.cjs",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"src"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"get-random-values-esm": "^1.0.2",
|
|
40
|
+
"lodash.startcase": "^4.4.0",
|
|
41
|
+
"@portabletext/schema": "0.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@sanity/pkg-utils": "^7.11.1",
|
|
45
|
+
"@sanity/schema": "^4.3.0",
|
|
46
|
+
"@sanity/types": "^4.3.0",
|
|
47
|
+
"@types/lodash.startcase": "^4.4.9",
|
|
48
|
+
"typescript": "^5.9.2"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@sanity/schema": "^4.3.0",
|
|
52
|
+
"@sanity/types": "^4.3.0"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=20.19"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "pkg-utils build --strict --check --clean",
|
|
62
|
+
"check:lint": "biome lint .",
|
|
63
|
+
"check:types": "tsc",
|
|
64
|
+
"check:types:watch": "tsc --watch",
|
|
65
|
+
"clean": "del .turbo && del lib && del node_modules",
|
|
66
|
+
"dev": "pkg-utils watch",
|
|
67
|
+
"lint:fix": "biome lint --write ."
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createPortableTextMemberSchemaTypes,
|
|
3
|
+
type PortableTextMemberSchemaTypes,
|
|
4
|
+
} from './portable-text-member-schema-types'
|
|
5
|
+
export {portableTextMemberSchemaTypesToSchema} from './portable-text-member-schema-types-to-schema'
|
|
6
|
+
export {compileSchemaDefinitionToPortableTextMemberSchemaTypes} from './schema-definition-to-portable-text-member-schema-types'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import getRandomValues from 'get-random-values-esm'
|
|
2
|
+
|
|
3
|
+
export const keyGenerator = (): string => randomKey(12)
|
|
4
|
+
|
|
5
|
+
const getByteHexTable = (() => {
|
|
6
|
+
let table: any[]
|
|
7
|
+
return () => {
|
|
8
|
+
if (table) {
|
|
9
|
+
return table
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
table = []
|
|
13
|
+
for (let i = 0; i < 256; ++i) {
|
|
14
|
+
table[i] = (i + 0x100).toString(16).slice(1)
|
|
15
|
+
}
|
|
16
|
+
return table
|
|
17
|
+
}
|
|
18
|
+
})()
|
|
19
|
+
|
|
20
|
+
// WHATWG crypto RNG - https://w3c.github.io/webcrypto/Overview.html
|
|
21
|
+
function whatwgRNG(length = 16) {
|
|
22
|
+
const rnds8 = new Uint8Array(length)
|
|
23
|
+
getRandomValues(rnds8)
|
|
24
|
+
return rnds8
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function randomKey(length?: number): string {
|
|
28
|
+
const table = getByteHexTable()
|
|
29
|
+
return whatwgRNG(length)
|
|
30
|
+
.reduce((str, n) => str + table[n], '')
|
|
31
|
+
.slice(0, length)
|
|
32
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {Schema} from '@portabletext/schema'
|
|
2
|
+
import type {PortableTextMemberSchemaTypes} from './portable-text-member-schema-types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @public
|
|
6
|
+
* Convert Sanity-specific schema types for Portable Text to a first-class
|
|
7
|
+
* Portable Text schema.
|
|
8
|
+
*/
|
|
9
|
+
export function portableTextMemberSchemaTypesToSchema(
|
|
10
|
+
schema: PortableTextMemberSchemaTypes,
|
|
11
|
+
): Schema {
|
|
12
|
+
return {
|
|
13
|
+
annotations: schema.annotations.map((annotation) => ({
|
|
14
|
+
name: annotation.name,
|
|
15
|
+
fields: annotation.fields.map((field) => ({
|
|
16
|
+
name: field.name,
|
|
17
|
+
type: field.type.jsonType,
|
|
18
|
+
title: field.type.title,
|
|
19
|
+
})),
|
|
20
|
+
title: annotation.title,
|
|
21
|
+
})),
|
|
22
|
+
block: {
|
|
23
|
+
name: schema.block.name,
|
|
24
|
+
},
|
|
25
|
+
blockObjects: schema.blockObjects.map((blockObject) => ({
|
|
26
|
+
name: blockObject.name,
|
|
27
|
+
fields: blockObject.fields.map((field) => ({
|
|
28
|
+
name: field.name,
|
|
29
|
+
type: field.type.jsonType,
|
|
30
|
+
title: field.type.title,
|
|
31
|
+
})),
|
|
32
|
+
title: blockObject.title,
|
|
33
|
+
})),
|
|
34
|
+
decorators: schema.decorators.map((decorator) => ({
|
|
35
|
+
name: decorator.value,
|
|
36
|
+
title: decorator.title,
|
|
37
|
+
value: decorator.value,
|
|
38
|
+
})),
|
|
39
|
+
inlineObjects: schema.inlineObjects.map((inlineObject) => ({
|
|
40
|
+
name: inlineObject.name,
|
|
41
|
+
fields: inlineObject.fields.map((field) => ({
|
|
42
|
+
name: field.name,
|
|
43
|
+
type: field.type.jsonType,
|
|
44
|
+
title: field.type.title,
|
|
45
|
+
})),
|
|
46
|
+
title: inlineObject.title,
|
|
47
|
+
})),
|
|
48
|
+
span: {
|
|
49
|
+
name: schema.span.name,
|
|
50
|
+
},
|
|
51
|
+
styles: schema.styles.map((style) => ({
|
|
52
|
+
name: style.value,
|
|
53
|
+
title: style.title,
|
|
54
|
+
value: style.value,
|
|
55
|
+
})),
|
|
56
|
+
lists: schema.lists.map((list) => ({
|
|
57
|
+
name: list.value,
|
|
58
|
+
title: list.title,
|
|
59
|
+
value: list.value,
|
|
60
|
+
})),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ArraySchemaType,
|
|
3
|
+
BlockDecoratorDefinition,
|
|
4
|
+
BlockListDefinition,
|
|
5
|
+
BlockSchemaType,
|
|
6
|
+
BlockStyleDefinition,
|
|
7
|
+
ObjectSchemaType,
|
|
8
|
+
PortableTextBlock,
|
|
9
|
+
SchemaType,
|
|
10
|
+
SpanSchemaType,
|
|
11
|
+
} from '@sanity/types'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
* Sanity-specific schema types for Portable Text.
|
|
16
|
+
*/
|
|
17
|
+
export type PortableTextMemberSchemaTypes = {
|
|
18
|
+
annotations: (ObjectSchemaType & {i18nTitleKey?: string})[]
|
|
19
|
+
block: ObjectSchemaType
|
|
20
|
+
blockObjects: ObjectSchemaType[]
|
|
21
|
+
decorators: BlockDecoratorDefinition[]
|
|
22
|
+
inlineObjects: ObjectSchemaType[]
|
|
23
|
+
portableText: ArraySchemaType<PortableTextBlock>
|
|
24
|
+
span: ObjectSchemaType
|
|
25
|
+
styles: BlockStyleDefinition[]
|
|
26
|
+
lists: BlockListDefinition[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @public
|
|
31
|
+
* Create Sanity-specific schema types for Portable Text from a Sanity array
|
|
32
|
+
* schema type.
|
|
33
|
+
*/
|
|
34
|
+
export function createPortableTextMemberSchemaTypes(
|
|
35
|
+
portableTextType: ArraySchemaType<PortableTextBlock>,
|
|
36
|
+
): PortableTextMemberSchemaTypes {
|
|
37
|
+
if (!portableTextType) {
|
|
38
|
+
throw new Error("Parameter 'portabletextType' missing (required)")
|
|
39
|
+
}
|
|
40
|
+
const blockType = portableTextType.of?.find(findBlockType) as
|
|
41
|
+
| BlockSchemaType
|
|
42
|
+
| undefined
|
|
43
|
+
if (!blockType) {
|
|
44
|
+
throw new Error('Block type is not defined in this schema (required)')
|
|
45
|
+
}
|
|
46
|
+
const childrenField = blockType.fields?.find(
|
|
47
|
+
(field) => field.name === 'children',
|
|
48
|
+
) as {type: ArraySchemaType} | undefined
|
|
49
|
+
if (!childrenField) {
|
|
50
|
+
throw new Error('Children field for block type found in schema (required)')
|
|
51
|
+
}
|
|
52
|
+
const ofType = childrenField.type.of
|
|
53
|
+
if (!ofType) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
'Valid types for block children not found in schema (required)',
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
const spanType = ofType.find((memberType) => memberType.name === 'span') as
|
|
59
|
+
| ObjectSchemaType
|
|
60
|
+
| undefined
|
|
61
|
+
if (!spanType) {
|
|
62
|
+
throw new Error('Span type not found in schema (required)')
|
|
63
|
+
}
|
|
64
|
+
const inlineObjectTypes = (ofType.filter(
|
|
65
|
+
(memberType) => memberType.name !== 'span',
|
|
66
|
+
) || []) as ObjectSchemaType[]
|
|
67
|
+
const blockObjectTypes = (portableTextType.of?.filter(
|
|
68
|
+
(field) => field.name !== blockType.name,
|
|
69
|
+
) || []) as ObjectSchemaType[]
|
|
70
|
+
return {
|
|
71
|
+
styles: resolveEnabledStyles(blockType),
|
|
72
|
+
decorators: resolveEnabledDecorators(spanType),
|
|
73
|
+
lists: resolveEnabledListItems(blockType),
|
|
74
|
+
block: blockType,
|
|
75
|
+
span: spanType,
|
|
76
|
+
portableText: portableTextType,
|
|
77
|
+
inlineObjects: inlineObjectTypes,
|
|
78
|
+
blockObjects: blockObjectTypes,
|
|
79
|
+
annotations: (spanType as SpanSchemaType).annotations,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function resolveEnabledStyles(blockType: ObjectSchemaType) {
|
|
84
|
+
const styleField = blockType.fields?.find(
|
|
85
|
+
(btField) => btField.name === 'style',
|
|
86
|
+
)
|
|
87
|
+
if (!styleField) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
"A field with name 'style' is not defined in the block type (required).",
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
const textStyles =
|
|
93
|
+
styleField.type.options?.list &&
|
|
94
|
+
styleField.type.options.list?.filter(
|
|
95
|
+
(style: {value: string}) => style.value,
|
|
96
|
+
)
|
|
97
|
+
if (!textStyles || textStyles.length === 0) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
'The style fields need at least one style ' +
|
|
100
|
+
"defined. I.e: {title: 'Normal', value: 'normal'}.",
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
return textStyles
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveEnabledDecorators(spanType: ObjectSchemaType) {
|
|
107
|
+
return (spanType as any).decorators
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolveEnabledListItems(blockType: ObjectSchemaType) {
|
|
111
|
+
const listField = blockType.fields?.find(
|
|
112
|
+
(btField) => btField.name === 'listItem',
|
|
113
|
+
)
|
|
114
|
+
if (!listField) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"A field with name 'listItem' is not defined in the block type (required).",
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
const listItems =
|
|
120
|
+
listField.type.options?.list &&
|
|
121
|
+
listField.type.options.list.filter((list: {value: string}) => list.value)
|
|
122
|
+
if (!listItems) {
|
|
123
|
+
throw new Error('The list field need at least to be an empty array')
|
|
124
|
+
}
|
|
125
|
+
return listItems
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function findBlockType(type: SchemaType): BlockSchemaType | null {
|
|
129
|
+
if (type.type) {
|
|
130
|
+
return findBlockType(type.type)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (type.name === 'block') {
|
|
134
|
+
return type as BlockSchemaType
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type {SchemaDefinition} from '@portabletext/schema'
|
|
2
|
+
import {Schema as SanitySchema} from '@sanity/schema'
|
|
3
|
+
import {defineField, defineType, type ObjectSchemaType} from '@sanity/types'
|
|
4
|
+
import startCase from 'lodash.startcase'
|
|
5
|
+
import {keyGenerator} from './key-generator'
|
|
6
|
+
import {
|
|
7
|
+
createPortableTextMemberSchemaTypes,
|
|
8
|
+
type PortableTextMemberSchemaTypes,
|
|
9
|
+
} from './portable-text-member-schema-types'
|
|
10
|
+
|
|
11
|
+
const temporaryImageName = `tmp-${keyGenerator()}-image`
|
|
12
|
+
const temporaryUrlName = `tmp-${keyGenerator()}-url`
|
|
13
|
+
|
|
14
|
+
const temporaryObjectNames: Record<string, string> = {
|
|
15
|
+
image: temporaryImageName,
|
|
16
|
+
url: temporaryUrlName,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const objectNames: Record<string, string> = {
|
|
20
|
+
[temporaryImageName]: 'image',
|
|
21
|
+
[temporaryUrlName]: 'url',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultObjectTitles: Record<string, string> = {
|
|
25
|
+
image: 'Image',
|
|
26
|
+
url: 'URL',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @public
|
|
31
|
+
* Compile a Portable Text schema definition to Sanity-specific schema types for
|
|
32
|
+
* Portable Text.
|
|
33
|
+
*/
|
|
34
|
+
export function compileSchemaDefinitionToPortableTextMemberSchemaTypes(
|
|
35
|
+
definition?: SchemaDefinition,
|
|
36
|
+
): PortableTextMemberSchemaTypes {
|
|
37
|
+
const blockObjects =
|
|
38
|
+
definition?.blockObjects?.map((blockObject) =>
|
|
39
|
+
defineType({
|
|
40
|
+
type: 'object',
|
|
41
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
42
|
+
// fields to objects with certain names.
|
|
43
|
+
name: temporaryObjectNames[blockObject.name] ?? blockObject.name,
|
|
44
|
+
title:
|
|
45
|
+
blockObject.title === undefined
|
|
46
|
+
? // This avoids the default title which is a title case of the object name
|
|
47
|
+
defaultObjectTitles[blockObject.name]
|
|
48
|
+
: blockObject.title,
|
|
49
|
+
fields:
|
|
50
|
+
blockObject.fields?.map((field) => ({
|
|
51
|
+
name: field.name,
|
|
52
|
+
type: field.type,
|
|
53
|
+
title: field.title ?? startCase(field.name),
|
|
54
|
+
})) ?? [],
|
|
55
|
+
}),
|
|
56
|
+
) ?? []
|
|
57
|
+
|
|
58
|
+
const inlineObjects =
|
|
59
|
+
definition?.inlineObjects?.map((inlineObject) =>
|
|
60
|
+
defineType({
|
|
61
|
+
type: 'object',
|
|
62
|
+
// Very naive way to work around `SanitySchema.compile` adding default
|
|
63
|
+
// fields to objects with certain names.
|
|
64
|
+
name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,
|
|
65
|
+
|
|
66
|
+
title:
|
|
67
|
+
inlineObject.title === undefined
|
|
68
|
+
? // This avoids the default title which is a title case of the object name
|
|
69
|
+
defaultObjectTitles[inlineObject.name]
|
|
70
|
+
: inlineObject.title,
|
|
71
|
+
fields:
|
|
72
|
+
inlineObject.fields?.map((field) => ({
|
|
73
|
+
name: field.name,
|
|
74
|
+
type: field.type,
|
|
75
|
+
title: field.title ?? startCase(field.name),
|
|
76
|
+
})) ?? [],
|
|
77
|
+
}),
|
|
78
|
+
) ?? []
|
|
79
|
+
|
|
80
|
+
const portableTextSchema = defineField({
|
|
81
|
+
type: 'array',
|
|
82
|
+
name: 'portable-text',
|
|
83
|
+
of: [
|
|
84
|
+
...blockObjects.map((blockObject) => ({type: blockObject.name})),
|
|
85
|
+
{
|
|
86
|
+
type: 'block',
|
|
87
|
+
name: 'block',
|
|
88
|
+
of: inlineObjects.map((inlineObject) => ({type: inlineObject.name})),
|
|
89
|
+
marks: {
|
|
90
|
+
decorators:
|
|
91
|
+
definition?.decorators?.map((decorator) => ({
|
|
92
|
+
title: decorator.title ?? startCase(decorator.name),
|
|
93
|
+
value: decorator.name,
|
|
94
|
+
})) ?? [],
|
|
95
|
+
annotations:
|
|
96
|
+
definition?.annotations?.map((annotation) => ({
|
|
97
|
+
name: annotation.name,
|
|
98
|
+
type: 'object',
|
|
99
|
+
title: annotation.title,
|
|
100
|
+
fields:
|
|
101
|
+
annotation.fields?.map((field) => ({
|
|
102
|
+
name: field.name,
|
|
103
|
+
title: field.title ?? startCase(field.name),
|
|
104
|
+
type: field.type,
|
|
105
|
+
})) ?? [],
|
|
106
|
+
})) ?? [],
|
|
107
|
+
},
|
|
108
|
+
lists:
|
|
109
|
+
definition?.lists?.map((list) => ({
|
|
110
|
+
value: list.name,
|
|
111
|
+
title: list.title ?? startCase(list.name),
|
|
112
|
+
})) ?? [],
|
|
113
|
+
styles:
|
|
114
|
+
definition?.styles?.map((style) => ({
|
|
115
|
+
value: style.name,
|
|
116
|
+
title: style.title ?? startCase(style.name),
|
|
117
|
+
})) ?? [],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const schema = SanitySchema.compile({
|
|
123
|
+
types: [portableTextSchema, ...blockObjects, ...inlineObjects],
|
|
124
|
+
}).get('portable-text')
|
|
125
|
+
|
|
126
|
+
const pteSchema = createPortableTextMemberSchemaTypes(schema)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
...pteSchema,
|
|
130
|
+
blockObjects: pteSchema.blockObjects.map((blockObject) =>
|
|
131
|
+
objectNames[blockObject.name] !== undefined
|
|
132
|
+
? ({
|
|
133
|
+
...blockObject,
|
|
134
|
+
name: objectNames[blockObject.name],
|
|
135
|
+
type: {
|
|
136
|
+
...blockObject.type,
|
|
137
|
+
name: objectNames[blockObject.name],
|
|
138
|
+
},
|
|
139
|
+
} as ObjectSchemaType)
|
|
140
|
+
: blockObject,
|
|
141
|
+
),
|
|
142
|
+
inlineObjects: pteSchema.inlineObjects.map((inlineObject) =>
|
|
143
|
+
objectNames[inlineObject.name] !== undefined
|
|
144
|
+
? ({
|
|
145
|
+
...inlineObject,
|
|
146
|
+
name: objectNames[inlineObject.name],
|
|
147
|
+
} as ObjectSchemaType)
|
|
148
|
+
: inlineObject,
|
|
149
|
+
),
|
|
150
|
+
} satisfies PortableTextMemberSchemaTypes
|
|
151
|
+
}
|