Skip to content

Commit 89a32f4

Browse files
author
Chris Sloop
committedApr 30, 2021
feat: update contentful-to-slatejs-adapter
1 parent 0c86f93 commit 89a32f4

File tree

4 files changed

+107
-97
lines changed

4 files changed

+107
-97
lines changed
 

‎packages/contentful-slatejs-adapter/src/__test__/contentful-to-slatejs-adapter.test.ts

+47-15
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,15 @@ describe('adapters', () => {
4242
[
4343
{
4444
type: Contentful.BLOCKS.PARAGRAPH,
45+
data: {},
46+
isVoid: false,
4547
children: [
46-
{ text: '' }
47-
]
48-
}
48+
{
49+
text: '',
50+
data: {},
51+
},
52+
],
53+
},
4954
],
5055
);
5156

@@ -60,8 +65,15 @@ describe('adapters', () => {
6065
[
6166
{
6267
type: Contentful.BLOCKS.PARAGRAPH,
68+
data: {},
69+
isVoid: false,
6370
children: [
64-
{ type: Contentful.INLINES.HYPERLINK }
71+
{
72+
type: Contentful.INLINES.HYPERLINK,
73+
data: {},
74+
isVoid: false,
75+
children: [],
76+
}
6577
]
6678
}
6779
],
@@ -73,8 +85,10 @@ describe('adapters', () => {
7385
[
7486
{
7587
type: Contentful.BLOCKS.PARAGRAPH,
88+
data: {},
89+
isVoid: false,
7690
children: [
77-
{ text: 'hi' }
91+
{ text: 'hi', data: {} }
7892
]
7993
}
8094
],
@@ -92,9 +106,11 @@ describe('adapters', () => {
92106
[
93107
{
94108
type: Contentful.BLOCKS.PARAGRAPH,
109+
data: {},
110+
isVoid: false,
95111
children: [
96-
{ text: 'this' },
97-
{ text: 'is', bold: true },
112+
{ text: 'this', data: {} },
113+
{ text: 'is', data: {}, bold: true },
98114
],
99115
},
100116
],
@@ -104,8 +120,10 @@ describe('adapters', () => {
104120
const slateDoc = [
105121
{
106122
type: Contentful.BLOCKS.PARAGRAPH,
123+
data: {},
124+
isVoid: false,
107125
children: [
108-
{ text: 'Hi' }
126+
{ text: 'Hi', data: {} }
109127
]
110128
}
111129
];
@@ -137,10 +155,12 @@ describe('adapters', () => {
137155
[
138156
{
139157
type: Contentful.BLOCKS.PARAGRAPH,
158+
data: {},
159+
isVoid: false,
140160
children: [
141-
{ text: 'this' },
142-
{ text: 'is', bold: true },
143-
{ text: 'huge', bold: true, italic: true },
161+
{ text: 'this', data: {} },
162+
{ text: 'is', data: {}, bold: true },
163+
{ text: 'huge', data: {}, bold: true, italic: true },
144164
],
145165
},
146166
],
@@ -162,15 +182,19 @@ describe('adapters', () => {
162182
[
163183
{
164184
type: Contentful.BLOCKS.PARAGRAPH,
185+
data: {},
186+
isVoid: false,
165187
children: [
166-
{ text: 'this is a test', bold: true },
167-
{ type: 'paragraph', underline: true },
188+
{ text: 'this is a test', data: {}, bold: true },
189+
{ text: 'paragraph', data: {}, underline: true },
168190
],
169191
},
170192
{
171193
type: Contentful.BLOCKS.PARAGRAPH,
194+
data: {},
195+
isVoid: false,
172196
children: [
173-
{ text: 'this is it' },
197+
{ text: 'this is it', data: {} },
174198
],
175199
},
176200
],
@@ -195,6 +219,8 @@ describe('adapters', () => {
195219
{
196220
type: Contentful.BLOCKS.PARAGRAPH,
197221
data: { a: 1 },
222+
isVoid: false,
223+
children: [],
198224
},
199225
],
200226
);
@@ -222,10 +248,13 @@ describe('adapters', () => {
222248
{
223249
type: Contentful.BLOCKS.PARAGRAPH,
224250
data: { a: 1 },
251+
isVoid: false,
225252
children: [
226253
{
227254
type: Contentful.INLINES.HYPERLINK,
228255
data: { a: 2 },
256+
isVoid: false,
257+
children: [],
229258
},
230259
],
231260
},
@@ -261,10 +290,12 @@ describe('adapters', () => {
261290
{
262291
type: Contentful.BLOCKS.PARAGRAPH,
263292
data: { a: 1 },
293+
isVoid: false,
264294
children: [
265295
{
266296
type: Contentful.INLINES.HYPERLINK,
267297
data: { a: 2 },
298+
isVoid: false,
268299
children: [
269300
{
270301
text: 'YO',
@@ -297,6 +328,7 @@ describe('adapters', () => {
297328
type: Contentful.BLOCKS.EMBEDDED_ENTRY,
298329
data: { a: 1 },
299330
isVoid: true,
331+
children: [],
300332
},
301333
],
302334
);
@@ -320,7 +352,7 @@ describe('adapters', () => {
320352
data: { a: 1 },
321353
isVoid: true,
322354
children: [
323-
{ text: '' },
355+
{ text: '', data: {} },
324356
]
325357
},
326358
];
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import flatmap from 'lodash.flatmap';
22

3+
import { fromJSON } from './schema';
4+
import { getDataOrDefault } from './helpers';
5+
6+
import type { Schema, SchemaJSON } from './schema';
37
import * as Contentful from '@contentful/rich-text-types';
4-
import { ContentfulNode, SlateNode, ContentfulNonTextNodes } from './types';
5-
import { getDataOfDefault } from './helpers';
6-
import { SchemaJSON, fromJSON, Schema } from './schema';
8+
import {
9+
ContentfulNode,
10+
ContentfulElementNode,
11+
SlateNode,
12+
SlateElement,
13+
SlateText,
14+
} from './types';
715

816
export interface ToSlatejsDocumentProperties {
917
document: Contentful.Document;
@@ -13,93 +21,51 @@ export interface ToSlatejsDocumentProperties {
1321
export default function toSlatejsDocument({
1422
document,
1523
schema,
16-
}: ToSlatejsDocumentProperties): Slate.Document {
17-
return {
18-
object: 'document',
19-
data: getDataOfDefault(document.data),
20-
nodes: flatmap(document.content, node => convertNode(node, fromJSON(schema))) as Slate.Block[],
21-
};
24+
}: ToSlatejsDocumentProperties): SlateNode[] {
25+
// TODO:
26+
// We allow adding data to the root document node, but Slate >v0.5.0
27+
// has no concept of a root document node. We should determine whether
28+
// this will be a compatibility problem for existing users.
29+
return flatmap(document.content, node => convertNode(node, fromJSON(schema)))
2230
}
2331

24-
function convertNode(node: ContentfulNode, schema: Schema): SlateNode[] {
25-
const nodes: SlateNode[] = [];
26-
32+
function convertNode(node: ContentfulNode, schema: Schema): SlateNode {
2733
if (node.nodeType === 'text') {
28-
const slateText = convertTextNode(node);
29-
30-
nodes.push(slateText);
34+
return convertTextNode(node as Contentful.Text);
3135
} else {
32-
const contentfulNode = node as ContentfulNonTextNodes;
36+
const contentfulNode = node as ContentfulElementNode;
3337
const childNodes = flatmap(contentfulNode.content, childNode => convertNode(childNode, schema));
34-
35-
const object = getSlateNodeObjectValue(contentfulNode.nodeType);
36-
let slateNode: SlateNode;
37-
if (object === 'inline') {
38-
slateNode = createInlineNode(contentfulNode, childNodes, schema);
39-
} else if (object === 'block') {
40-
slateNode = createBlockNode(contentfulNode, childNodes, schema);
41-
} else {
42-
throw new Error(`Unexpected slate object '${object}'`);
43-
}
44-
nodes.push(slateNode);
38+
const slateNode = convertElementNode(contentfulNode, childNodes, schema);
39+
return slateNode;
4540
}
46-
return nodes;
4741
}
4842

49-
function createBlockNode(
50-
contentfulBlock: ContentfulNonTextNodes,
51-
childNodes: SlateNode[],
43+
function convertElementNode(
44+
contentfulBlock: ContentfulElementNode,
45+
children: SlateNode[],
5246
schema: Schema,
53-
): Slate.Block {
47+
): SlateElement {
5448
return {
55-
object: 'block',
5649
type: contentfulBlock.nodeType,
57-
nodes: childNodes,
50+
children,
5851
isVoid: schema.isVoid(contentfulBlock),
59-
data: getDataOfDefault(contentfulBlock.data),
60-
} as Slate.Block;
52+
data: getDataOrDefault(contentfulBlock.data),
53+
};
6154
}
6255

63-
function createInlineNode(
64-
contentfulBlock: ContentfulNonTextNodes,
65-
childNodes: SlateNode[],
66-
schema: Schema,
67-
): Slate.Inline {
56+
function convertTextNode(node: Contentful.Text): SlateText {
6857
return {
69-
object: 'inline',
70-
type: contentfulBlock.nodeType,
71-
nodes: childNodes,
72-
isVoid: schema.isVoid(contentfulBlock),
73-
data: getDataOfDefault(contentfulBlock.data),
74-
} as Slate.Inline;
75-
}
76-
77-
function convertTextNode(node: ContentfulNode): Slate.Text {
78-
const { marks = [], value, data } = node as Contentful.Text;
79-
const slateText: Slate.Text = {
80-
object: 'text',
81-
leaves: [
82-
{
83-
object: 'leaf',
84-
text: value,
85-
marks: marks.map(mark => ({
86-
...mark,
87-
data: {},
88-
object: 'mark',
89-
})),
90-
} as Slate.TextLeaf,
91-
],
92-
data: getDataOfDefault(data),
58+
text: node.value,
59+
data: getDataOrDefault(node.data),
60+
...convertTextMarks(node)
9361
};
94-
return slateText;
9562
}
9663

97-
function getSlateNodeObjectValue(nodeType: string): 'inline' | 'block' {
98-
if (Object.values(Contentful.BLOCKS).includes(nodeType)) {
99-
return 'block';
100-
} else if (Object.values(Contentful.INLINES).includes(nodeType)) {
101-
return 'inline';
102-
} else {
103-
throw new Error(`Unexpected contentful nodeType '${nodeType}'`);
64+
type SlateMarks = { [mark: string]: true };
65+
function convertTextMarks(node: Contentful.Text): SlateMarks {
66+
const marks: SlateMarks = {};
67+
for (const mark of node.marks) {
68+
marks[mark.type] = true;
10469
}
70+
return marks;
10571
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/**
22
* Ensures that data defaults to an empty object.
33
*/
4-
export const getDataOfDefault = (value?: Record<string, any>) => value || {};
4+
export const getDataOrDefault = (value?: Record<string, any>) => value || {};

‎packages/contentful-slatejs-adapter/src/types/index.ts

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
import * as Contentful from '@contentful/rich-text-types';
2-
import type { BaseText } from 'slate';
32

4-
type SlateText = BaseText & {
3+
export type SlateText = {
4+
text: string;
5+
data: object;
6+
// This is a workaround for TypeScript's limitations around
7+
// index property exclusion. Ideally we'd join the above properties
8+
// with something like
9+
//
10+
// & { [mark: string]: string }
11+
//
12+
// but TypeScript doesn't allow us to create such objects, only
13+
// work around inconsistencies in existing JavaScript.
14+
//
15+
// In reality Slate's node marks are arbitrary, but for this library
16+
// denoting marks used by the tests as optional should be okay.
517
bold?: boolean;
618
italic?: boolean;
719
underline?: boolean;
820
};
9-
type SlateElement = {
21+
export type SlateElement = {
1022
type: string;
11-
children?: SlateNode[];
12-
data?: object;
13-
isVoid?: boolean;
23+
data: object;
24+
isVoid: boolean;
25+
children: SlateNode[];
1426
};
1527

16-
export type ContentfulNode = Contentful.Block | Contentful.Inline | Contentful.Text;
28+
export type ContentfulElementNode = Contentful.Block | Contentful.Inline;
29+
export type ContentfulNode = ContentfulElementNode | Contentful.Text;
1730
export type SlateNode = SlateElement | SlateText;
18-
export type ContentfulNonTextNodes = Contentful.Block | Contentful.Inline;

0 commit comments

Comments
 (0)
Please sign in to comment.