Skip to content

Commit 784a650

Browse files
committed
remove unnecessary functions, refactor
1 parent 1dba2ad commit 784a650

File tree

14 files changed

+1962
-542
lines changed

14 files changed

+1962
-542
lines changed

README.md

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ Install the package via npm
2121

2222
## Features
2323

24+
### API Overview
25+
26+
![Module API](media/resolver-api-overview.png)
27+
2428
### Parsing rich text HTML to a JSON tree
2529

2630
The tool provides environment-aware (browser or Node.js) `parseHtml` function to transform HTML into an array of simplified JSON trees. Any valid HTML is parsed, including all attributes. Together with built-in transformation methods, this tool is a suitable option for processing HTML and rich text from external sources, to make it compatible with Kontent.ai rich text format. See dedicated [JSON transformer docs](docs/index.md) for further information.
@@ -35,6 +39,11 @@ The tool provides environment-aware (browser or Node.js) `parseHtml` function to
3539
- Vue: [vue-portabletext](https://github.com/portabletext/vue-portabletext)
3640
- Astro: [astro-portabletext](https://github.com/theisel/astro-portabletext)
3741

42+
> [!TIP]
43+
> This module re-exports modified `toHTML` function and `<PortableText>` component from `to-html` and `react-portabletext` packages respectively. These modified helpers provide default resolution for tags which are either unsupported or only partially supported in the original packages (`sub` and `sup` tags, images, tables and links).
44+
>
45+
> Make sure to use these re-exports if you want to take advantage of the default resolution.
46+
3847
The tool provides `transformToPortableText` function to convert rich text content into an array of Portable Text blocks, with custom blocks defined for Kontent.ai-specific objects.
3948

4049
Combined with a suitable package for the framework of your choice, this makes for an optimal solution for resolving rich text.
@@ -56,12 +65,6 @@ https://github.com/kontent-ai/rich-text-resolver-js/blob/6fe68490a32bb304d141cff
5665

5766
https://github.com/kontent-ai/rich-text-resolver-js/blob/6fe68490a32bb304d141cff741fb7e57001550eb/showcase/showcase.ts#L14-L24
5867

59-
> [!TIP]
60-
> Package provides helpers for image resolution:
61-
> * React: `ImageComponent` component, accepting `PortableTextImage` as a prop.
62-
> * HTML: `resolveImage` function, accepting `PortableTextImage` and an optional custom resolver.
63-
> * Vue: `resolveImageVue` function, accepting `PortableTextImage`, Vue render function and an optional custom resolver.
64-
6568
##### Item link – **PortableTextItemLink**
6669

6770
https://github.com/kontent-ai/rich-text-resolver-js/blob/6fe68490a32bb304d141cff741fb7e57001550eb/showcase/showcase.ts#L26-L34
@@ -70,49 +73,8 @@ https://github.com/kontent-ai/rich-text-resolver-js/blob/6fe68490a32bb304d141cff
7073

7174
https://github.com/kontent-ai/rich-text-resolver-js/blob/6fe68490a32bb304d141cff741fb7e57001550eb/showcase/showcase.ts#L36-L62
7275

73-
> [!TIP]
74-
> Package provides helpers for table resolution:
75-
> * React: `TableComponent` component, accepting `PortableTextTable` as a prop.
76-
> * HTML: `resolveTable` function, accepting `PortableTextTable` and an optional custom resolver.
77-
> * Vue: `resolveTableVue` function, accepting `PortableTextTable`, Vue render function and an optional custom resolver.
78-
7976
## Examples
8077

81-
### Modifying portable text nodes
82-
83-
Package exports a `traversePortableText` method, which accepts an array of `PortableTextObject` and a callback function. The method recursively traverses all nodes and their subnodes, optionally modifying them with the provided callback:
84-
85-
```ts
86-
import {
87-
PortableTextObject,
88-
transformToPortableText,
89-
traversePortableText,
90-
} from "@kontent-ai/rich-text-resolver";
91-
92-
const input = `<figure data-asset-id="guid" data-image-id="guid"><img src="https://asseturl.xyz" data-asset-id="guid" data-image-id="guid" alt=""></figure>`;
93-
94-
// Adds height parameter to asset reference and changes _type.
95-
const processBlocks = (block: PortableTextObject) => {
96-
if (block._type === "image") {
97-
const modifiedReference = {
98-
...block.asset,
99-
height: 300
100-
}
101-
102-
return {
103-
...block,
104-
asset: modifiedReference,
105-
_type: "modifiedImage"
106-
}
107-
}
108-
109-
// logic for modifying other object types...
110-
}
111-
112-
const portableText = transformToPortableText(input);
113-
const modifiedPortableText = traversePortableText(portableText, processBlocks);
114-
```
115-
11678
### Plain HTML resolution
11779

11880
HTML resolution using a slightly modified version of `toHTML` function from `@portabletext/to-html` package.
@@ -184,7 +146,6 @@ import {
184146
} from "@kontent-ai/rich-text-resolver/utils/react";
185147
import {
186148
transformToPortableText,
187-
resolveTable,
188149
} from "@kontent-ai/rich-text-resolver";
189150

190151
// assumes richTextElement from SDK
@@ -195,7 +156,7 @@ const resolvers: PortableTextReactResolvers = {
195156
const item = richTextElement.linkedItems.find(item => item.system.codename === value.component._ref);
196157
return <div>{item?.elements.text_element.value}</div>;
197158
},
198-
// default components for table and image for convenience
159+
// Image and Table components are used as a default fallback if a resolver isn't explicitly specified
199160
table: ({ value }) => <TableComponent {...value} />,
200161
image: ({ value }) => <ImageComponent {...value} />,
201162
},
@@ -286,6 +247,42 @@ const components: PortableTextComponents = {
286247
</template>
287248
```
288249

250+
### Modifying portable text nodes
251+
252+
Package exports a `traversePortableText` method, which accepts an array of `PortableTextObject` and a callback function. The method recursively traverses all nodes and their subnodes, optionally modifying them with the provided callback:
253+
254+
```ts
255+
import {
256+
PortableTextObject,
257+
transformToPortableText,
258+
traversePortableText,
259+
} from "@kontent-ai/rich-text-resolver";
260+
261+
const input = `<figure data-asset-id="guid" data-image-id="guid"><img src="https://asseturl.xyz" data-asset-id="guid" data-image-id="guid" alt=""></figure>`;
262+
263+
// Adds height parameter to asset reference and changes _type.
264+
const processBlocks = (block: PortableTextObject) => {
265+
if (block._type === "image") {
266+
const modifiedReference = {
267+
...block.asset,
268+
height: 300
269+
}
270+
271+
return {
272+
...block,
273+
asset: modifiedReference,
274+
_type: "modifiedImage"
275+
}
276+
}
277+
278+
// logic for modifying other object types...
279+
}
280+
281+
const portableText = transformToPortableText(input);
282+
const modifiedPortableText = traversePortableText(portableText, processBlocks);
283+
```
284+
285+
289286
### MAPI transformation
290287

291288
`toManagementApiFormat` is a custom transformation method built upon `toHTML` package, allowing you to restore portable text previously created from management API rich text back into MAPI supported format.
@@ -303,9 +300,9 @@ const components: PortableTextComponents = {
303300
```
304301

305302
> [!IMPORTANT]
306-
> MAPI transformation logic expects Portable Text that had been previously created from management API rich text and performs only minimal validation.
303+
> MAPI transformation logic expects Portable Text that had been previously created from management API rich text and performs only minimal validation. It doesn't provide implicit transformation capabilities from other formats (such as delivery API).
307304
>
308-
> Transformation from other formats (such as delivery API) is not supported unless the blocks are manually adjusted to be MAPI compatible prior to running the method.
305+
> If you're interested in transforming external HTML or rich text to a MAPI compatible format, see [JSON transformer docs](docs/index.md) instead.
309306
310307
[last-commit]: https://img.shields.io/github/last-commit/kontent-ai/rich-text-resolver-js?style=for-the-badge
311308
[contributors-shield]: https://img.shields.io/github/contributors/kontent-ai/rich-text-resolver-js?style=for-the-badge

docs/index.md

Lines changed: 11 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# JSON Transformers
22

3-
This module provides an environment-aware (browser or Node.js) `parseHtml` function to convert an HTML string into an array of nodes. The JSON structure can subsequently be transformed using one of the provided transformation methods, either to a modified HTML string or a completely different structure, both in synchronous and asynchronous manner.
3+
This module provides an environment-aware (browser or Node.js) `parseHtml` function to convert an HTML string into an array of nodes. The resulting array can subsequently be modified by one of the provided functions and transformed back to HTML.
44

55
This toolset can be particularly useful for transforming rich text or HTML content from external sources into a valid Kontent.ai rich text format in migration scenarios.
66

@@ -34,38 +34,38 @@ export interface DomHtmlNode<TAttributes = Record<string, string | undefined>> {
3434
}
3535
```
3636

37-
The resulting array can be transformed using one of the functions included in the module.
38-
3937
### HTML Transformation
4038

4139
To transform the `DomNode` array back to HTML, you can use `nodesToHtml` function or its async variant `nodesToHtmlAsync`. The function accepts the parsed array and a `transformers` object, which defines custom transformation for each HTML node. Text nodes are transformed automatically. A wildcard `*` can be used to define fallback transformation for remaining tags. If no explicit or wildcard transformation is provided, default resolution is used.
4240

4341
#### Basic
4442
Basic example of HTML transformation, removing HTML attribute `style` and transforming `b` tag to `strong`:
4543
```ts
46-
import { nodesToHtml, NodeToStringMap, parseHtml } from '@kontent-ai/rich-text-resolver';
44+
import { nodesToHtml, NodeToHtmlMap, parseHtml } from '@kontent-ai/rich-text-resolver';
4745

4846
const rawHtml = `<p style="color:red">Hello <b>World!</b></p>`;
4947
const parsedNodes = parseHtml(rawHtml);
5048

51-
const transformers: NodeToStringMap = {
49+
const transformers: NodeToHtmlMap = {
5250
// children contains already transformed child nodes
5351
b: (node, children) => `<strong>${children}</strong>`;
52+
53+
// wildcard transformation removes attributes from remaining nodes
5454
"*": (node, children) => `<${node.tagName}>${children}</${node.tagName}>`;
5555
};
5656

5757
// restores original HTML with attributes
5858
const defaultOutput = nodesToHtml(parsedNodes, {});
5959
console.log(defaultOutput); // <p style="color:red">Hello <b>World!</b></p>
6060

61-
// b is converted to strong, wildcard transformation omits attributes from remaining nodes
61+
// b is converted to strong, wildcard transformation is used for remaining nodes
6262
const customOutput = nodesToHtml(parsedNodes, transformers);
6363
console.log(customOutput); // <p>Hello <strong>World!</strong></p>
6464
```
6565

6666
#### Advanced
6767

68-
For more complex scenarios, optional context and its handler can be passed to the top level transformation function (`nodesToHtml` or its async variant) as third and fourth parameters respectively.
68+
For more complex scenarios, optional context and its handler can be passed to `nodesToHtml` as third and fourth parameters respectively.
6969

7070
The context can then be accessed in individual transformations, defined in the `transformers` object. If you need to dynamically update the context, you may optionally provide a context handler, which accepts current node and context as parameters and passes a cloned, modified context for child node processing, ensuring each node gets valid contextual data.
7171

@@ -82,15 +82,15 @@ For that matter, we will use `nodesToHtmlAsync` method and pass an instance of J
8282
import { ManagementClient } from "@kontent-ai/management-sdk";
8383
import {
8484
parseHtml,
85-
AsyncNodeToStringMap,
85+
AsyncNodeToHtmlMap,
8686
nodesToHtmlAsync,
8787
} from "@kontent-ai/rich-text-resolver";
8888

8989
const input = `<img src="https://website.com/image.jpg" alt="some image">`;
9090
const nodes = parseHtml(input);
9191

9292
// type parameter specifies context type, in this case ManagementClient
93-
const transformers: AsyncNodeToStringMap<ManagementClient> = {
93+
const transformers: AsyncNodeToHtmlMap<ManagementClient> = {
9494
// context (client) can be accessed as a third parameter in each transformation
9595
img: async (node, _, client) =>
9696
await new Promise<string>(async (resolve, reject) => {
@@ -155,7 +155,7 @@ In this case, we can store depth as a context and increment it via handler anyti
155155
import {
156156
nodesToHtml,
157157
DomNode,
158-
NodeToStringMap,
158+
NodeToHtmlMap,
159159
parseHtml,
160160
} from "@kontent-ai/rich-text-resolver";
161161

@@ -178,7 +178,7 @@ const depthHandler = (node: DomNode, context: DepthContext): DepthContext =>
178178
? { ...context, divSpanDepth: context.divSpanDepth + 1 } // return new context with incremented depth
179179
: context; // return the same context if not div/span
180180

181-
const transformers: NodeToStringMap<DepthContext> = {
181+
const transformers: NodeToHtmlMap<DepthContext> = {
182182
// we'll only define transformations for 'div' and 'span'. Default resolution will transform remaining tags.
183183
div: (_, children, context) =>
184184
// topmost div is at depth=1, as context is updated before processing.
@@ -201,56 +201,4 @@ console.log(output);
201201

202202
```
203203

204-
### Generic transformation
205-
206-
Should you need to transform the nodes to a different structure, rather than HTML (string), you can use `transformNodes` (`transformNodesAsync`) function. Its usage is similar but revolves around generics and provides further control over the output, such as specifying custom transformation for text nodes.
207-
208-
Snippet showcasing use of `transformNodes` to convert the `DomNode` array into Portable Text, as used internally in this module. Full source code in [the corresponding TS file](../src/transformers/portable-text-transformer/portable-text-transformer.ts).
209-
210-
```ts
211-
// context stores current list type and list item depth
212-
type ListContext = {
213-
depth: number;
214-
type: "number" | "bullet" | "unknown";
215-
};
216-
217-
const transformers: NodeTransformers<PortableTextItem, ListContext> = {
218-
text: processText,
219-
tag: {
220-
...(Object.fromEntries(
221-
blockElements.map((tagName) => [tagName, processBlock]),
222-
) as Record<BlockElement, NodeToPortableText<DomHtmlNode>>),
223-
...(Object.fromEntries(
224-
textStyleElements.map((tagName) => [tagName, processMark]),
225-
) as Record<TextStyleElement, NodeToPortableText<DomHtmlNode>>),
226-
...(Object.fromEntries(
227-
ignoredElements.map((tagName) => [tagName, ignoreProcessing]),
228-
) as Record<IgnoredElement, NodeToPortableText<DomHtmlNode>>),
229-
a: processMark,
230-
li: processListItem,
231-
table: processTable,
232-
tr: processTableRow,
233-
td: processTableCell,
234-
br: processLineBreak,
235-
img: processImage,
236-
object: processLinkedItemOrComponent,
237-
},
238-
};
239-
240-
const updateListContext = (node: DomNode, context: ListContext): ListContext =>
241-
(isElement(node) && isListBlock(node))
242-
? { depth: context.depth + 1, type: node.tagName === "ol" ? "number" : "bullet" }
243-
: context;
244-
245-
export const nodesToPortableText = (
246-
parsedNodes: DomNode[],
247-
): PortableTextObject[] =>
248-
transformNodes(
249-
parsedNodes,
250-
transformers,
251-
{ depth: 0, type: "unknown" }, // initialization of list transformation context
252-
updateListContext,
253-
) as PortableTextObject[];
254-
```
255-
256204

index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export { transformNodes, transformNodesAsync, nodesToHtml, nodesToHtmlAsync, NodeTransformer, NodeTransformers, AsyncNodeTransformer, AsyncNodeTransformers, NodeToString, NodeToStringAsync, NodeToStringMap, AsyncNodeToStringMap } from './src/index.js';
1+
export { nodesToHtml, nodesToHtmlAsync, NodeToHtml, NodeToHtmlAsync, NodeToHtmlMap, AsyncNodeToHtmlMap } from './src/index.js';
22
export { traversePortableText } from './src/utils/transformer-utils.js';
3-
export { nodesToPortableText, transformToPortableText } from "./src/transformers/portable-text-transformer/portable-text-transformer.js";
3+
export { transformToPortableText } from "./src/transformers/portable-text-transformer/portable-text-transformer.js";
44
export { parseHtml } from "./src/parser/index.js";
55
export { resolveImage, resolveTable, toHTMLImageDefault, PortableTextHtmlResolvers, toHTML } from "./src/utils/resolution/html.js";
66
export { resolveImage as resolveImageVue, resolveTable as resolveTableVue, toVueImageDefault } from "./src/utils/resolution/vue.js";

media/domtree.jpg

-24.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)