diff --git a/packages/fern-docs/mdx/src/index.ts b/packages/fern-docs/mdx/src/index.ts index 9d9dccf7ec..cebfa42637 100644 --- a/packages/fern-docs/mdx/src/index.ts +++ b/packages/fern-docs/mdx/src/index.ts @@ -1,4 +1,7 @@ -export type { MdxJsxAttribute } from "mdast-util-mdx"; +export type { + MdxJsxAttribute, + MdxJsxExpressionAttribute, +} from "mdast-util-mdx"; export type { MDXComponents } from "mdx/types"; export type { PluggableList } from "unified"; export { diff --git a/packages/fern-docs/ui/package.json b/packages/fern-docs/ui/package.json index 549dea871e..fbe55be7fc 100644 --- a/packages/fern-docs/ui/package.json +++ b/packages/fern-docs/ui/package.json @@ -81,6 +81,7 @@ "colorjs.io": "^0.5.2", "es-toolkit": "^1.27.0", "esbuild": "0.24.2", + "estree-walker": "^3.0.3", "fastdom": "^1.0.12", "framer-motion": "^11.2.4", "github-slugger": "^2.0.0", diff --git a/packages/fern-docs/ui/src/components/FernImage.tsx b/packages/fern-docs/ui/src/components/FernImage.tsx index 431af1bbb6..8dc2861db1 100644 --- a/packages/fern-docs/ui/src/components/FernImage.tsx +++ b/packages/fern-docs/ui/src/components/FernImage.tsx @@ -52,7 +52,10 @@ export const FernImage = forwardRef< // nextjs requires a strict allowlist of hosts for // so we'll fall back to if the host is not in the allowlist (or if no custom loader is provided) - if ((!host || !NEXT_IMAGE_HOSTS.includes(host)) && !loader) { + if ( + ((!host || !NEXT_IMAGE_HOSTS.includes(host)) && !loader) || + (!width && !height) + ) { return ( { + if (isMdxJsxElementHast(node)) { + node.attributes.forEach((attr) => { + const estree = getEstree(attr); + if (estree == null) { + return; + } + walk(estree, { + enter(node) { + if (node.type === "Literal" && typeof node.value === "string") { + // TODO: if the replaced src is a Image (contains width and height), we need to add them to the parent JSX root somehow. + // for example: } /> -> } /> + // currently, we cannot leverage NextJS Image Optimization for this edge case. + node.value = + options.replaceSrc?.(node.value)?.src ?? node.value; + } + }, + }); + }); + } + }); }; } + +function getEstree(attr: MdxJsxAttribute | MdxJsxExpressionAttribute) { + if ( + attr.type === "mdxJsxAttribute" && + attr.value && + typeof attr.value !== "string" && + attr.value.type === "mdxJsxAttributeValueExpression" && + attr.value.data?.estree + ) { + return attr.value.data?.estree; + } else if (attr.type === "mdxJsxExpressionAttribute" && attr.data?.estree) { + return attr.data?.estree; + } + return null; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4038ab8dc3..5769418294 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -797,7 +797,7 @@ importers: version: 5.13.0 braintrust: specifier: ^0.0.182 - version: 0.0.182(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0))(react@18.3.1)(sswr@2.1.0(svelte@5.19.2))(svelte@5.19.2)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8) + version: 0.0.182(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0))(react@18.3.1)(sswr@2.1.0(svelte@5.19.2))(svelte@5.19.2)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8) cssnano: specifier: ^6.0.3 version: 6.1.2(postcss@8.4.31) @@ -2160,6 +2160,9 @@ importers: esbuild: specifier: 0.24.2 version: 0.24.2 + estree-walker: + specifier: ^3.0.3 + version: 3.0.3 fastdom: specifier: ^1.0.12 version: 1.0.12 @@ -7477,7 +7480,7 @@ packages: resolution: {integrity: sha512-GWrNeElMYHO8FVETjW205u2s9IXFs46fmVKY8T1dHgksCm3JV8w4k14gM2eaZbOUOH/tGcOuz5YbqJl8iKkA8w==} engines: {node: '>=18.0.0'} peerDependencies: - next: ^13.5.0 || ^14.0.0 || ^15.0.0 + next: npm:@fern-api/next@14.2.9-fork.2 react: 18.3.1 react-dom: 18.3.1 storybook: ^8.4.4 @@ -18683,16 +18686,6 @@ snapshots: '@smithy/types': 3.6.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0)': - dependencies: - '@aws-sdk/client-sts': 3.682.0 - '@aws-sdk/core': 3.723.0 - '@aws-sdk/types': 3.723.0 - '@smithy/property-provider': 4.0.0 - '@smithy/types': 4.1.0 - tslib: 2.8.1 - optional: true - '@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0)': dependencies: '@aws-sdk/client-sts': 3.723.0 @@ -24839,9 +24832,9 @@ snapshots: transitivePeerDependencies: - typescript - '@vercel/functions@1.5.2(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0))': + '@vercel/functions@1.5.2(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0))': optionalDependencies: - '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.723.0) '@vercel/kv@2.0.0': dependencies: @@ -26746,12 +26739,12 @@ snapshots: dependencies: fill-range: 7.1.1 - braintrust@0.0.182(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0))(react@18.3.1)(sswr@2.1.0(svelte@5.19.2))(svelte@5.19.2)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8): + braintrust@0.0.182(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0))(react@18.3.1)(sswr@2.1.0(svelte@5.19.2))(svelte@5.19.2)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8): dependencies: '@ai-sdk/provider': 1.0.4 '@braintrust/core': 0.0.76 '@next/env': 14.2.9 - '@vercel/functions': 1.5.2(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0)) + '@vercel/functions': 1.5.2(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0)) ai: 3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.19.2))(svelte@5.19.2)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8) argparse: 2.0.1 chalk: 4.1.2