Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add unstable_htmlAsRichText and unstable_markdownAsRichText helpers #342

Merged
merged 18 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/richtext/errors/PrismicRichTextError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class PrismicRichTextError extends Error {}
3 changes: 3 additions & 0 deletions src/richtext/errors/PrismicRichTextSerializerError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PrismicRichTextError } from "./PrismicRichTextError";

export class PrismicRichTextSerializerError extends PrismicRichTextError {}
26 changes: 12 additions & 14 deletions src/richtext/filterRichTextField.ts
angeloashmore marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { TitleField } from "../types/value/title";
/**
* Get allowed nodes types from a rich text or title field custom type model.
*
* @param model - A rich text or title field custom type model
* @param model - A rich text or title field custom type model.
*
* @returns An array of allowed nodes types
* @returns An array of allowed nodes types.
*/
const getAllowedNodeTypes = (
model: CustomTypeModelRichTextField,
Expand Down Expand Up @@ -44,10 +44,10 @@ const getAllowedNodeTypes = (
/**
* Filter an image node based on given model.
*
* @param node - An image node to filter
* @param model - A rich text or title field custom type model
* @param node - An image node to filter.
* @param model - A rich text or title field custom type model.
*
* @returns A filtered image node based on the given model
* @returns A filtered image node based on the given model.
*/
const filterImageNode = (
node: RTImageNode,
Expand All @@ -72,11 +72,11 @@ const filterImageNode = (
/**
* Filter a text node based on given model.
*
* @param node - A text node to filter
* @param model - A rich text or title field custom type model
* @param allowedNodeTypes - An array of allowed nodes types
* @param node - A text node to filter.
* @param model - A rich text or title field custom type model.
* @param allowedNodeTypes - An array of allowed nodes types.
*
* @returns A filtered text node based on the given model
* @returns A filtered text node based on the given model.
*/
const filterTextNode = (
node: RTTextNode,
Expand Down Expand Up @@ -123,12 +123,10 @@ const filterTextNode = (
/**
* Filter a rich text field based on given model.
*
* @param richTextField - A rich text or title field from Prismic
* @param model - A rich text or title field custom type model
* @param richTextField - A rich text or title field from Prismic.
* @param model - A rich text or title field custom type model.
*
* @returns A rich text or title field filtered based on the given model
*
* @experimental - This API is subject to change and might not follow SemVer.
* @returns A rich text or title field filtered based on the given model.
*/
export const filterRichTextField = <Field extends RichTextField | TitleField>(
richTextField: Field,
Expand Down
29 changes: 0 additions & 29 deletions src/richtext/htmlAsRichText.ts

This file was deleted.

10 changes: 6 additions & 4 deletions src/richtext/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export { filterRichTextField } from "./filterRichTextField";

export { RichTextNodeType as Element } from "../types/value/richText";

export { htmlAsRichText } from "./htmlAsRichText";
export { markdownAsRichText } from "./markdownAsRichText";
export { unstable_htmlAsRichText } from "./unstable_htmlAsRichText";
export { unstable_markdownAsRichText } from "./unstable_markdownAsRichText";

export { PrismicRichTextError } from "./errors/PrismicRichTextError";
export { PrismicRichTextSerializerError } from "./errors/PrismicRichTextSerializerError";

export type {
RichTextFunctionSerializer,
RichTextMapSerializer,
RichTextMapSerializerFunction,
RichTextHTMLMapSerializer,
} from "./types";

export type { RichTextHTMLMapSerializer } from "./utils/hastUtilToRichText";
39 changes: 0 additions & 39 deletions src/richtext/markdownAsRichText.ts

This file was deleted.

71 changes: 62 additions & 9 deletions src/richtext/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { VFile } from "vfile";
import { Element } from "hast";

import {
RTAnyNode,
Expand All @@ -11,22 +11,23 @@ import {
RTHeading5Node,
RTHeading6Node,
RTImageNode,
RTInlineNode,
RTLabelNode,
RTLinkNode,
RTListItemNode,
RTListNode,
RTNode,
RTOListItemNode,
RTOListNode,
RTParagraphNode,
RTPreformattedNode,
RTSpanNode,
RTStrongNode,
RichTextField,
RichTextNodeType,
RichTextNodeTypes,
} from "../types/value/richText";

import { RehypeRichTextConfig } from "./utils/rehypeRichText";
import { RTPartialInlineNode } from "./utils/RichTextFieldBuilder";

// Serializers

Expand Down Expand Up @@ -158,15 +159,67 @@ export const RichTextReversedNodeType = {
[RichTextNodeType.oList]: "oList",
} as const;

// hast serializers

/**
* Configuration that determines the output of `*AsRichText` functions.
* A shorthand definition for {@link RichTextHTMLMapSerializer} rich text node
* types.
*
* @remarks
* The `label` rich text node type is not available as is. Use an object
* containing your label name to convert to label nodes instead. For example:
* `u: { label: "underline" }`
* @remarks
* The `span` rich text node type is not available as it is not relevant in the
* context of going from HTML to Prismic rich text.
*/
export type AsRichTextConfig = RehypeRichTextConfig;
export type RichTextHTMLMapSerializerShorthand =
| Exclude<RichTextNodeTypes, "label" | "span">
| { label: string };

/**
* The return type of `*AsRichText` functions.
* The payload provided to a {@link RichTextHTMLMapSerializerFunction}.
*/
export type AsRichTextReturnType = {
result: RichTextField;
warnings: VFile["messages"];
type RichTextHTMLMapSerializerFunctionPayload = {
/**
* The hast {@link Element} node to serialize.
*/
node: Element;

/**
* Additional context information to help with the serialization.
*/
context: {
/**
* The list type of the last list node encountered if any.
*/
listType?: "group-list-item" | "group-o-list-item";
};
};

/**
* Serializes a hast {@link Element} node to a
* {@link RichTextHTMLMapSerializerShorthand} or a rich text node.
*
* @remarks
* Serializing to a rich text node directly is not recommended and is only
* available as an escape hatch. Prefer returning a
* {@link RichTextHTMLMapSerializerShorthand} instead.
*/
export type RichTextHTMLMapSerializerFunction = (
payload: RichTextHTMLMapSerializerFunctionPayload,
) =>
| RichTextHTMLMapSerializerShorthand
| RTNode
| RTInlineNode
| RTPartialInlineNode
| undefined;

/**
* Serializes a hast {@link Element} node matching the given HTML tag name or CSS
* selector to a Prismic rich text node.
*/
export type RichTextHTMLMapSerializer = Record<
string,
RichTextHTMLMapSerializerShorthand | RichTextHTMLMapSerializerFunction
>;
42 changes: 42 additions & 0 deletions src/richtext/unstable_htmlAsRichText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import rehypeParse from "rehype-parse";
import { unified } from "unified";

import { RichTextField } from "../types/value/richText";

import { RehypeRichTextConfig, rehypeRichText } from "./utils/rehypeRichText";

/**
* Configuration that determines the output of {@link htmlAsRichText}.
*/
export type HTMLAsRichTextConfig = RehypeRichTextConfig;

/**
* The return type of {@link htmlAsRichText}.
*/
export type HTMLAsRichTextReturnType = {
result: RichTextField;
warnings: string[];
};

/**
* Converts an HTML string to a rich text field.
*
* @param html - An HTML string.
* @param config - Configuration that determines the output the function.
*
* @returns `html` as rich text.
*
* @experimental Names and implementations may change in the future.
* `unstable_htmlAsRichText()` does not follow SemVer.
*/
export const unstable_htmlAsRichText = (
html: string,
config?: HTMLAsRichTextConfig,
): HTMLAsRichTextReturnType => {
const { result, messages } = unified()
.use(rehypeParse, { emitParseErrors: true, missingDoctype: 0 })
.use(rehypeRichText, config)
.processSync(html);

return { result, warnings: messages.map((message) => message.toString()) };
};
52 changes: 52 additions & 0 deletions src/richtext/unstable_markdownAsRichText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import { unified } from "unified";

import { RichTextField } from "../types/value/richText";

import { RehypeRichTextConfig, rehypeRichText } from "./utils/rehypeRichText";

// Used for TSDocs only.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { unstable_htmlAsRichText } from "./unstable_htmlAsRichText";

/**
* Configuration that determines the output of {@link markdownAsRichText}.
*/
export type MarkdownAsRichTextConfig = RehypeRichTextConfig;

/**
* The return type of {@link markdownAsRichText}.
*/
export type MarkdownAsRichTextReturnType = {
result: RichTextField;
warnings: string[];
};

/**
* Converts a markdown string to a rich text field.
*
* @remarks
* To convert markdown to a rich text field, this function first converts it to
* HTML. It's essentially a sugar above {@link unstable_htmlAsRichText}.
*
* @param markdown - A markdown string.
* @param config - Configuration that determines the output of the function.
*
* @returns `markdown` as rich text.
*
* @experimental Names and implementations may change in the future.
* `unstable_markdownAsRichText()` does not follow SemVer.
*/
export const unstable_markdownAsRichText = (
markdown: string,
config?: MarkdownAsRichTextConfig,
): MarkdownAsRichTextReturnType => {
const { result, messages } = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeRichText, config)
.processSync(markdown);

return { result, warnings: messages.map((message) => message.toString()) };
};
Loading
Loading