Skip to content

Commit

Permalink
feat: render optimized images and mdx parser fixes (#710)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Apr 26, 2024
1 parent 7fb7d6c commit e9a96c9
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/ui/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@types/estree": "^1.0.5",
"@types/estree-jsx": "^1.0.5",
"@types/hast": "^3.0.4",
"@types/jsonpath": "^0.2.4",
"@types/lodash-es": "^4.17.12",
Expand Down
37 changes: 30 additions & 7 deletions packages/ui/app/src/mdx/base-components.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMounted } from "@fern-ui/react-commons";
import { DocsV1Read } from "@fern-api/fdr-sdk";
import cn from "clsx";
import { toNumber } from "lodash-es";
import {
AnchorHTMLAttributes,
Children,
Expand All @@ -9,11 +10,14 @@ import {
FC,
isValidElement,
ReactElement,
useMemo,
} from "react";
import Zoom from "react-medium-image-zoom";
import { AbsolutelyPositionedAnchor } from "../commons/AbsolutelyPositionedAnchor";
import { FernCard } from "../components/FernCard";
import { FernImage } from "../components/FernImage";
import { FernLink } from "../components/FernLink";
import { useDocsContext } from "../contexts/docs-context/useDocsContext";
import { useNavigationContext } from "../contexts/navigation-context";
import { onlyText } from "../util/onlyText";
import "./base-components.scss";
Expand Down Expand Up @@ -130,14 +134,33 @@ function isImgElement(element: ReactElement): element is ReactElement<ImgProps>
return element.type === Img;
}

export const Img: FC<ImgProps> = ({ className, src, alt, disableZoom, ...rest }) => {
const mounted = useMounted();
if (!mounted || disableZoom) {
return <img {...rest} className={cn(className, "max-w-full")} src={src} alt={alt} />;
}
export const Img: FC<ImgProps> = ({ className, src, height, width, disableZoom, ...rest }) => {
const { files } = useDocsContext();
// const mounted = useMounted();
// if (!mounted || disableZoom) {
// return <img {...rest} className={cn(className, "max-w-full")} src={src} alt={alt} />;
// }
const fernImageSrc = useMemo((): DocsV1Read.File_ | undefined => {
if (src == null) {
return undefined;
}

if (src.startsWith("file:")) {
const fileId = src.slice(5);
return files[fileId];
}

return { type: "url", url: src };
}, [files, src]);
return (
<Zoom>
<img {...rest} className={cn(className, "max-w-full")} src={src} alt={alt} />
{/* <img {...rest} className={cn(className, "max-w-full")} src={src} alt={alt} /> */}
<FernImage
src={fernImageSrc}
{...rest}
height={height != null ? toNumber(height) : undefined}
width={height != null ? toNumber(width) : undefined}
/>
</Zoom>
);
};
5 changes: 5 additions & 0 deletions packages/ui/app/src/mdx/mdx.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DocsV1Read } from "@fern-api/fdr-sdk";
import type { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import rehypeKatex from "rehype-katex";
Expand Down Expand Up @@ -154,10 +155,12 @@ function withDefaultMdxOptions({
export async function maybeSerializeMdxContent(
content: string,
mdxOptions?: FernSerializeMdxOptions,
files?: Record<DocsV1Read.FileId, DocsV1Read.File_>,
): Promise<MDXRemoteSerializeResult | string>;
export async function maybeSerializeMdxContent(
content: string | undefined,
mdxOptions?: FernSerializeMdxOptions,
files?: Record<DocsV1Read.FileId, DocsV1Read.File_>,
): Promise<MDXRemoteSerializeResult | string | undefined>;
export async function maybeSerializeMdxContent(
content: string | undefined,
Expand Down Expand Up @@ -199,10 +202,12 @@ export async function maybeSerializeMdxContent(
export async function serializeMdxWithFrontmatter(
content: string,
mdxOptions?: FernSerializeMdxOptions,
files?: Record<DocsV1Read.FileId, DocsV1Read.File_>,
): Promise<SerializedMdxContent>;
export async function serializeMdxWithFrontmatter(
content: string | undefined,
mdxOptions?: FernSerializeMdxOptions,
files?: Record<DocsV1Read.FileId, DocsV1Read.File_>,
): Promise<SerializedMdxContent | undefined>;
export async function serializeMdxWithFrontmatter(
content: string | undefined,
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/app/src/mdx/plugins/makeToc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function makeToc(tree: Root): ElementContent {
if (
getBooleanValue(
node.attributes.find((attr) => isMdxJsxAttribute(attr) && attr.name === "toc")?.value,
) === false
) !== true
) {
return SKIP;
}
Expand Down Expand Up @@ -64,7 +64,7 @@ export function makeToc(tree: Root): ElementContent {
const itemsAttr = attributes.find((attr) => attr.name === "tabs");
const tocAttr = attributes.find((attr) => attr.name === "toc");
const parentSkipToc =
tocAttr != null && typeof tocAttr.value === "object" && tocAttr.value?.value === "false";
tocAttr == null || (typeof tocAttr.value === "object" && tocAttr.value?.value !== "true");
if (itemsAttr?.value == null || typeof itemsAttr.value === "string") {
return;
}
Expand All @@ -91,7 +91,7 @@ export function makeToc(tree: Root): ElementContent {
const itemsAttr = attributes.find((attr) => attr.name === "items");
const tocAttr = attributes.find((attr) => attr.name === "toc");
const parentSkipToc =
tocAttr != null && typeof tocAttr.value === "object" && tocAttr.value?.value === "false";
tocAttr == null || (typeof tocAttr.value === "object" && tocAttr.value?.value !== "true");

if (itemsAttr?.value == null || typeof itemsAttr.value === "string") {
return;
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/app/src/mdx/plugins/rehypeFernComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ export function rehypeFernComponents(): (tree: Root) => void {
}

if (isMdxJsxFlowElement(node) && node.name != null) {
if (node.name === "Tabs" && node.children.length > 0) {
if (node.name === "Tabs") {
transformTabs(node, index, parent);
}

if (node.name === "TabGroup" && node.children.length > 0) {
if (node.name === "TabGroup") {
transformTabs(node, index, parent);
}

if (node.name === "AccordionGroup" && node.children.length > 0) {
if (node.name === "AccordionGroup") {
transformAccordionGroup(node, index, parent);
}
}
Expand Down
54 changes: 51 additions & 3 deletions packages/ui/app/src/mdx/plugins/rehypeSanitizeJSX.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Expression } from "estree";
import type { JSXFragment } from "estree-jsx";
import { SKIP as ESTREE_SKIP, visit as visitEstree } from "estree-util-visit";
import type { ElementContent, Root } from "hast";
import { MdxJsxAttribute, MdxJsxExpressionAttribute } from "mdast-util-mdx-jsx";
import { SKIP, visit } from "unist-util-visit";
import { parseStringStyle } from "../../util/parseStringStyle";
import { INTRINSIC_JSX_TAGS } from "../common/intrinsict-elements";
Expand Down Expand Up @@ -31,7 +32,7 @@ export function rehypeSanitizeJSX({ showErrors = false }: { showErrors?: boolean
attr.value?.type === "mdxJsxAttributeValueExpression" &&
attr.value.data?.estree != null
) {
visitEstree(attr.value.data.estree, (esnode, _key, _i, ancestors) => {
visitEstree(attr.value.data.estree, (esnode, _key, i, ancestors) => {
if (ancestors.length === 0) {
return undefined;
}
Expand All @@ -45,13 +46,23 @@ export function rehypeSanitizeJSX({ showErrors = false }: { showErrors?: boolean
ancestor.expression = jsxFragment();
return ESTREE_SKIP;
}
if (ancestor.type === "JSXFragment" && i != null) {
ancestor.children[i] = jsxFragment();
}
}
return undefined;
});
}

return attr;
});

// const temporaryRoot: Root = {
// type: "root",
// children: node.children,
// };
// rehypeSanitizeJSX({ showErrors })(temporaryRoot);
// node.children = temporaryRoot.children as ElementContent[];
}
}
return;
Expand Down Expand Up @@ -92,9 +103,46 @@ export function rehypeSanitizeJSX({ showErrors = false }: { showErrors?: boolean
});
}
});

// convert img to img element
visit(tree, (node, index, parent) => {
if (index == null) {
return;
}
if (isMdxJsxFlowElement(node)) {
if (node.name === "img") {
const properties = toProperties(node.attributes);
if (properties != null) {
parent?.children.splice(index, 1, {
type: "element",
tagName: "img",
properties,
children: node.children,
});
}
}
}
});
};
}

function toProperties(attributes: (MdxJsxAttribute | MdxJsxExpressionAttribute)[]): Record<string, string> | undefined {
const properties: Record<string, string> = {};
for (const attr of attributes) {
if (attr.type === "mdxJsxAttribute" && attr.value != null) {
if (typeof attr.value === "string") {
properties[attr.name] = attr.value;
} else {
// todo: handle literal expressions
return undefined;
}
} else if (attr.type === "mdxJsxExpressionAttribute") {
return undefined;
}
}
return properties;
}

function mdxErrorBoundary(nodeName: string): ElementContent {
return {
type: "mdxJsxFlowElement",
Expand All @@ -104,7 +152,7 @@ function mdxErrorBoundary(nodeName: string): ElementContent {
};
}

function jsxFragment(): Expression {
function jsxFragment(): JSXFragment {
return {
type: "JSXFragment",
openingFragment: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/mdx/plugins/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { unknownToString } from "../../util/unknownToString";
import { valueToEstree } from "./to-estree";

export function isMdxJsxFlowElement(node: Node): node is MdxJsxFlowElementHast {
return node.type === "mdxJsxFlowElement";
return node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
}

export function isMdxJsxAttribute(
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e9a96c9

Please sign in to comment.