diff --git a/www/packages/docs-utils/package.json b/www/packages/docs-utils/package.json
index c5c5c4a773..b12cd17325 100644
--- a/www/packages/docs-utils/package.json
+++ b/www/packages/docs-utils/package.json
@@ -28,7 +28,9 @@
"watch": "tsc --watch"
},
"dependencies": {
+ "@mdx-js/mdx": "^3.1.0",
"remark-frontmatter": "^5.0.0",
+ "remark-mdx": "^3.1.0",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"to-vfile": "^8.0.0",
diff --git a/www/packages/remark-rehype-plugins/src/utils/estree-to-js.ts b/www/packages/docs-utils/src/estree-to-js.ts
similarity index 98%
rename from www/packages/remark-rehype-plugins/src/utils/estree-to-js.ts
rename to www/packages/docs-utils/src/estree-to-js.ts
index 2073ec2904..3726e29042 100644
--- a/www/packages/remark-rehype-plugins/src/utils/estree-to-js.ts
+++ b/www/packages/docs-utils/src/estree-to-js.ts
@@ -7,7 +7,7 @@ import {
ExpressionJsVarLiteral,
LiteralExpression,
ObjectExpression,
-} from "../types/index.js"
+} from "types"
export function estreeToJs(estree: Estree) {
// TODO improve on this utility. Currently it's implemented to work
diff --git a/www/packages/remark-rehype-plugins/src/utils/expression-is-utils.ts b/www/packages/docs-utils/src/expression-is-utils.ts
similarity index 85%
rename from www/packages/remark-rehype-plugins/src/utils/expression-is-utils.ts
rename to www/packages/docs-utils/src/expression-is-utils.ts
index 455c3ef7e2..b08a0bea26 100644
--- a/www/packages/remark-rehype-plugins/src/utils/expression-is-utils.ts
+++ b/www/packages/docs-utils/src/expression-is-utils.ts
@@ -1,4 +1,4 @@
-import { ExpressionJsVarLiteral, ExpressionJsVarObj } from "../types/index.js"
+import { ExpressionJsVarLiteral, ExpressionJsVarObj } from "types"
export function isExpressionJsVarLiteral(
expression: unknown
diff --git a/www/packages/docs-utils/src/get-clean-md.ts b/www/packages/docs-utils/src/get-clean-md.ts
new file mode 100644
index 0000000000..bb8af46259
--- /dev/null
+++ b/www/packages/docs-utils/src/get-clean-md.ts
@@ -0,0 +1,136 @@
+import remarkMdx from "remark-mdx"
+import remarkParse from "remark-parse"
+import remarkStringify from "remark-stringify"
+import { read } from "to-vfile"
+import { UnistNode, UnistNodeWithData, UnistTree } from "types"
+import { Plugin, Transformer, unified } from "unified"
+import { SKIP } from "unist-util-visit"
+import type { VFile } from "vfile"
+import {
+ parseCard,
+ parseCardList,
+ parseCodeTabs,
+ parseDetails,
+ parseNote,
+ parsePrerequisites,
+ parseSourceCodeLink,
+ parseTable,
+ parseTabs,
+ parseTypeList,
+ parseWorkflowDiagram,
+} from "./utils/parse-elms.js"
+
+const parseComponentsPlugin = (): Transformer => {
+ return async (tree) => {
+ const { visit } = await import("unist-util-visit")
+
+ let pageTitle = ""
+
+ visit(
+ tree as UnistTree,
+ ["mdxJsxFlowElement", "element", "mdxjsEsm", "heading"],
+ (node: UnistNode, index, parent) => {
+ if (typeof index !== "number" || !parent) {
+ return
+ }
+ if (
+ node.type === "mdxjsEsm" &&
+ node.value?.startsWith("export const metadata = ") &&
+ node.data &&
+ "estree" in node.data
+ ) {
+ const regexMatch = /title: (?
.+),?/.exec(node.value)
+ if (regexMatch?.groups?.title) {
+ pageTitle = regexMatch.groups.title
+ .replace(/,$/, "")
+ .replaceAll(/\$\{.+\}/g, "")
+ .replaceAll(/^['"`]/g, "")
+ .replaceAll(/['"`]$/g, "")
+ .trim()
+ }
+ }
+ if (node.type === "heading") {
+ if (
+ node.depth === 1 &&
+ node.children?.length &&
+ node.children[0].value === "metadata.title"
+ ) {
+ node.children[0] = {
+ type: "text",
+ value: pageTitle,
+ }
+ }
+ return
+ }
+ if (
+ node.type === "mdxjsEsm" ||
+ node.name === "Feedback" ||
+ node.name === "ChildDocs" ||
+ node.name === "DetailsList"
+ ) {
+ parent?.children.splice(index, 1)
+ return [SKIP, index]
+ }
+ switch (node.name) {
+ case "Card":
+ return parseCard(node, index, parent)
+ case "CardList":
+ return parseCardList(node as UnistNodeWithData, index, parent)
+ case "CodeTabs":
+ return parseCodeTabs(node as UnistNodeWithData, index, parent)
+ case "Details":
+ return parseDetails(node as UnistNodeWithData, index, parent)
+ case "Note":
+ return parseNote(node, index, parent)
+ case "Prerequisites":
+ return parsePrerequisites(node as UnistNodeWithData, index, parent)
+ case "SourceCodeLink":
+ return parseSourceCodeLink(node as UnistNodeWithData, index, parent)
+ case "Table":
+ return parseTable(node as UnistNodeWithData, index, parent)
+ case "Tabs":
+ return parseTabs(node as UnistNodeWithData, index, parent)
+ case "TypeList":
+ return parseTypeList(node as UnistNodeWithData, index, parent)
+ case "WorkflowDiagram":
+ return parseWorkflowDiagram(
+ node as UnistNodeWithData,
+ index,
+ parent
+ )
+ }
+ }
+ )
+ }
+}
+
+const getParsedAsString = (file: VFile): string => {
+ return file.toString().replaceAll(/^([\s]*)\* /gm, "$1- ")
+}
+
+export const getCleanMd = async (
+ filePath: string,
+ plugins?: {
+ before?: Plugin[]
+ after?: Plugin[]
+ }
+): Promise => {
+ if (!filePath.endsWith(".md") && !filePath.endsWith(".mdx")) {
+ return ""
+ }
+ const unifier = unified().use(remarkParse).use(remarkMdx).use(remarkStringify)
+
+ plugins?.before?.forEach((plugin) => {
+ unifier.use(...(Array.isArray(plugin) ? plugin : [plugin]))
+ })
+
+ unifier.use(parseComponentsPlugin)
+
+ plugins?.after?.forEach((plugin) => {
+ unifier.use(...(Array.isArray(plugin) ? plugin : [plugin]))
+ })
+
+ const parsed = await unifier.process(await read(filePath))
+
+ return getParsedAsString(parsed)
+}
diff --git a/www/packages/docs-utils/src/index.ts b/www/packages/docs-utils/src/index.ts
index ba4bdd1147..aa40bbaf00 100644
--- a/www/packages/docs-utils/src/index.ts
+++ b/www/packages/docs-utils/src/index.ts
@@ -1,4 +1,7 @@
+export * from "./estree-to-js.js"
+export * from "./expression-is-utils.js"
export * from "./find-title.js"
+export * from "./get-clean-md.js"
export * from "./get-file-slug-sync.js"
export * from "./get-file-slug.js"
export * from "./get-front-matter.js"
diff --git a/www/packages/docs-utils/src/utils/parse-elms.ts b/www/packages/docs-utils/src/utils/parse-elms.ts
new file mode 100644
index 0000000000..c67779c8a3
--- /dev/null
+++ b/www/packages/docs-utils/src/utils/parse-elms.ts
@@ -0,0 +1,597 @@
+import { ExpressionJsVar, UnistNode, UnistNodeWithData, UnistTree } from "types"
+import { SKIP, VisitorResult } from "unist-util-visit"
+import { estreeToJs } from "../estree-to-js.js"
+import {
+ isExpressionJsVarLiteral,
+ isExpressionJsVarObj,
+} from "../expression-is-utils.js"
+
+export const parseCard = (
+ node: UnistNode,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ let title: string | undefined,
+ text: string | undefined,
+ href: string | undefined
+ node.attributes?.some((attr) => {
+ if (title && text && href) {
+ return true
+ }
+ if (attr.name === "title") {
+ title = attr.value as string
+ } else if (attr.name === "text") {
+ text = attr.value as string
+ } else if (attr.name === "href") {
+ href = attr.value as string
+ }
+ return false
+ })
+ if (!title || !href) {
+ return
+ }
+ parent?.children.splice(index, 1, {
+ type: "paragraph",
+ children: [
+ {
+ type: "link",
+ url: href,
+ children: [
+ {
+ type: "text",
+ value: title,
+ },
+ ],
+ },
+ {
+ type: "text",
+ value: `: ${text}`,
+ },
+ ],
+ })
+ return [SKIP, index]
+}
+
+export const parseCardList = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const items = node.attributes?.find((attr) => attr.name === "items")
+ if (!items || typeof items.value === "string" || !items.value.data?.estree) {
+ return
+ }
+
+ const itemsJsVar = estreeToJs(items.value.data.estree)
+
+ if (!itemsJsVar || !Array.isArray(itemsJsVar)) {
+ return
+ }
+
+ const listItems = itemsJsVar
+ .map((item) => {
+ if (
+ !isExpressionJsVarObj(item) ||
+ !("text" in item) ||
+ !("link" in item) ||
+ !isExpressionJsVarLiteral(item.text) ||
+ !isExpressionJsVarLiteral(item.link)
+ ) {
+ return null
+ }
+ return {
+ type: "listItem",
+ children: [
+ {
+ type: "paragraph",
+ children: [
+ {
+ type: "link",
+ url: `#${item.link.data}`,
+ children: [
+ {
+ type: "text",
+ value: item.text.data,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ }
+ })
+ .filter(Boolean) as UnistNode[]
+
+ parent?.children.splice(index, 1, {
+ type: "list",
+ ordered: false,
+ spread: false,
+ children: listItems,
+ })
+ return [SKIP, index]
+}
+
+export const parseCodeTabs = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const tabs = node.children?.filter((child) => child.name === "CodeTab")
+ if (!tabs) {
+ return
+ }
+
+ const children: UnistNode[] = []
+
+ tabs.forEach((tab) => {
+ const label = tab.attributes?.find((attr) => attr.name === "label")
+ const code = tab.children?.find((child) => child.type === "code")
+
+ if (!label || !code) {
+ return
+ }
+
+ children.push({
+ type: "mdxJsxFlowElement",
+ name: "details",
+ children: [
+ {
+ type: "mdxJsxFlowElement",
+ name: "summary",
+ children: [
+ {
+ type: "text",
+ value: (label.value as string) || "summary",
+ },
+ ],
+ },
+ code,
+ ],
+ })
+ })
+
+ parent?.children.splice(index, 1, ...children)
+ return [SKIP, index]
+}
+
+export const parseDetails = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const summary = node.attributes?.find(
+ (attr) => attr.name === "summaryContent"
+ )
+
+ parent?.children.splice(index, 1, {
+ type: "mdxJsxFlowElement",
+ name: "details",
+ children: [
+ {
+ type: "mdxJsxFlowElement",
+ name: "summary",
+ children: [
+ {
+ type: "text",
+ value: (summary?.value as string) || "Details",
+ },
+ ],
+ },
+ ...(node.children || []),
+ ],
+ })
+ return [SKIP, index]
+}
+
+export const parseNote = (
+ node: UnistNode,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ parent.children?.splice(index, 1, ...(node.children || []))
+ return [SKIP, index]
+}
+
+export const parsePrerequisites = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const items = node.attributes?.find((attr) => attr.name === "items")
+ if (!items || typeof items.value === "string" || !items.value.data?.estree) {
+ return
+ }
+
+ const itemsJsVar = estreeToJs(items.value.data.estree)
+
+ if (!itemsJsVar || !Array.isArray(itemsJsVar)) {
+ return
+ }
+
+ const listItems = itemsJsVar
+ .map((item) => {
+ if (
+ !isExpressionJsVarObj(item) ||
+ !("text" in item) ||
+ !("link" in item) ||
+ !isExpressionJsVarLiteral(item.text) ||
+ !isExpressionJsVarLiteral(item.link)
+ ) {
+ return null
+ }
+ return {
+ type: "listItem",
+ children: [
+ {
+ type: "paragraph",
+ children: [
+ {
+ type: "link",
+ url: `${item.link.data}`,
+ children: [
+ {
+ type: "text",
+ value: item.text.data,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ }
+ })
+ .filter(Boolean) as UnistNode[]
+
+ parent?.children.splice(
+ index,
+ 1,
+ {
+ type: "heading",
+ depth: 3,
+ children: [
+ {
+ type: "text",
+ value: "Prerequisites",
+ },
+ ],
+ },
+ {
+ type: "list",
+ ordered: false,
+ spread: false,
+ children: listItems,
+ }
+ )
+ return [SKIP, index]
+}
+
+export const parseSourceCodeLink = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const link = node.attributes?.find((attr) => attr.name === "link")
+ if (!link) {
+ return
+ }
+
+ parent?.children.splice(index, 1, {
+ type: "paragraph",
+ children: [
+ {
+ type: "link",
+ url: link?.value as string,
+ children: [
+ {
+ type: "text",
+ value: "Source code",
+ },
+ ],
+ },
+ ],
+ })
+ return [SKIP, index]
+}
+
+export const parseTable = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const headerNode = node.children?.find(
+ (child) => child.name === "Table.Header"
+ )
+ const bodyNode = node.children?.find((child) => child.name === "Table.Body")
+
+ let nodeText = ``
+ let headerCellsCount = 0
+
+ headerNode?.children?.forEach((headerRow, rowIndex) => {
+ if (rowIndex > 0) {
+ nodeText += `\n`
+ }
+ const childCells =
+ headerRow.children?.length === 1 &&
+ headerRow.children[0].type === "paragraph"
+ ? headerRow.children[0].children
+ : headerRow.children
+ headerCellsCount = childCells?.length || 0
+ childCells?.forEach((headerCell) => {
+ if (headerCell.name !== "Table.HeaderCell") {
+ return
+ }
+ nodeText += `|`
+ nodeText += formatNodeText(getTextNode(headerCell))
+ })
+ nodeText += `|`
+ })
+
+ nodeText += `\n|${new Array(headerCellsCount).fill(`---`).join(`|`)}|`
+
+ bodyNode?.children?.forEach((bodyRow) => {
+ nodeText += `\n`
+ bodyRow.children?.forEach((bodyCell) => {
+ nodeText += `|`
+ nodeText += formatNodeText(getTextNode(bodyCell))
+ })
+ nodeText += `|`
+ })
+
+ parent.children?.splice(index, 1, {
+ type: "paragraph",
+ children: [
+ {
+ type: "text",
+ value: nodeText,
+ },
+ ],
+ })
+}
+
+export const parseTabs = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ if ((node.children?.length || 0) < 2) {
+ return
+ }
+
+ const tabs: UnistNode[] = []
+
+ node.children![0].children?.forEach((tabList) => {
+ tabList.children?.forEach((tabTrigger, index) => {
+ const tabContentNode = node.children?.[1].children?.[index]
+ const tabLabel = formatNodeText(getTextNode(tabTrigger))
+ const tabContent = tabContentNode?.children || []
+ if (!tabLabel || !tabContent) {
+ return
+ }
+
+ tabs.push({
+ type: "mdxJsxFlowElement",
+ name: "details",
+ children: [
+ {
+ type: "mdxJsxFlowElement",
+ name: "summary",
+ children: [
+ {
+ type: "text",
+ value: tabLabel,
+ },
+ ],
+ },
+ ...tabContent,
+ ],
+ })
+ })
+ })
+
+ parent.children?.splice(index, 1, ...tabs)
+ return [SKIP, index]
+}
+
+export const parseTypeList = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const types = node.attributes?.find((attr) => attr.name === "types")
+ if (!types || typeof types.value === "string" || !types.value.data?.estree) {
+ return
+ }
+
+ const typesJsVar = estreeToJs(types.value.data.estree)
+
+ if (!typesJsVar || !Array.isArray(typesJsVar)) {
+ return
+ }
+
+ const generateTypeListItems = (
+ typesJsVar: ExpressionJsVar[]
+ ): UnistNode[] => {
+ const listItems: UnistNode[] = []
+
+ typesJsVar.forEach((item) => {
+ if (!isExpressionJsVarObj(item)) {
+ return
+ }
+
+ const typeName = isExpressionJsVarLiteral(item.name) ? item.name.data : ""
+ const itemType = isExpressionJsVarLiteral(item.type) ? item.type.data : ""
+ const itemDescription = isExpressionJsVarLiteral(item.description)
+ ? item.description.data
+ : ""
+
+ if (!typeName || !itemType) {
+ return
+ }
+
+ const itemListChildren = item.children || []
+ const children: UnistNode[] = [
+ {
+ type: "paragraph",
+ children: [
+ {
+ type: "text",
+ value: `${typeName}: (${itemType}) ${itemDescription}`,
+ },
+ ],
+ },
+ ]
+
+ if (Array.isArray(itemListChildren) && itemListChildren.length) {
+ children.push(...generateTypeListItems(itemListChildren))
+ }
+
+ listItems.push({
+ type: "listItem",
+ children,
+ })
+ })
+
+ return listItems
+ }
+
+ const listItems = generateTypeListItems(typesJsVar)
+
+ parent?.children.splice(index, 1, {
+ type: "list",
+ ordered: false,
+ spread: false,
+ children: listItems,
+ })
+ return [SKIP, index]
+}
+
+export const parseWorkflowDiagram = (
+ node: UnistNodeWithData,
+ index: number,
+ parent: UnistTree
+): VisitorResult => {
+ const worflowItems = node.attributes?.find((attr) => attr.name === "workflow")
+ if (
+ !worflowItems ||
+ typeof worflowItems.value === "string" ||
+ !worflowItems.value.data?.estree
+ ) {
+ return
+ }
+
+ const workflowJsVar = estreeToJs(worflowItems.value.data.estree)
+
+ if (
+ !isExpressionJsVarObj(workflowJsVar) ||
+ !("steps" in workflowJsVar) ||
+ !Array.isArray(workflowJsVar.steps)
+ ) {
+ return
+ }
+
+ const generateWorkflowItems = (
+ jsVarItems: ExpressionJsVar[]
+ ): UnistNode[] => {
+ const listItems: UnistNode[] = []
+
+ jsVarItems.forEach((item) => {
+ if (!isExpressionJsVarObj(item)) {
+ return
+ }
+
+ const stepName = isExpressionJsVarLiteral(item.name) ? item.name.data : ""
+ const stepDescription = isExpressionJsVarLiteral(item.description)
+ ? item.description.data
+ : ""
+ const stepLink = isExpressionJsVarLiteral(item.link)
+ ? (item.link.data as string)
+ : `#${stepName}`
+
+ if (!stepName) {
+ return
+ }
+
+ const stepChildren = item.steps || []
+ const children: UnistNode[] = [
+ {
+ type: "paragraph",
+ children: [
+ {
+ type: "link",
+ url: stepLink,
+ children: [
+ {
+ type: "text",
+ value: `${stepName}`,
+ },
+ ],
+ },
+ {
+ type: "text",
+ value: `: ${stepDescription}`,
+ },
+ ],
+ },
+ ]
+
+ if (Array.isArray(stepChildren) && stepChildren.length) {
+ children.push(...generateWorkflowItems(stepChildren))
+ }
+
+ listItems.push({
+ type: "listItem",
+ children,
+ })
+ })
+
+ return listItems
+ }
+
+ const listItems = generateWorkflowItems(workflowJsVar.steps)
+
+ parent?.children.splice(index, 1, {
+ type: "list",
+ ordered: false,
+ spread: false,
+ children: listItems,
+ })
+ return [SKIP, index]
+}
+
+/**
+ * Helpers
+ */
+const getTextNode = (node: UnistNode): UnistNode | undefined => {
+ let textNode: UnistNode | undefined
+ node.children?.some((child) => {
+ if (textNode) {
+ return true
+ }
+ if (child.type === "text") {
+ textNode = child
+ } else if (child.type === "paragraph") {
+ textNode = getTextNode(child)
+ } else if (child.value) {
+ textNode = child
+ }
+
+ return false
+ })
+
+ return textNode
+}
+
+const formatNodeText = (node?: UnistNode): string => {
+ if (!node) {
+ return ""
+ }
+ if (node.type === "inlineCode") {
+ return `\`${node.value}\``
+ } else if (node.type === "code") {
+ return `\`\`\`${"lang" in node ? node.lang : "ts"}\n${node.value}\n\`\`\``
+ } else if (node.type === "link") {
+ return `[${node.children?.[0].value}](${node.url})`
+ }
+
+ return node.value || ""
+}
diff --git a/www/packages/remark-rehype-plugins/src/add-url-to-relative-link.ts b/www/packages/remark-rehype-plugins/src/add-url-to-relative-link.ts
new file mode 100644
index 0000000000..c947848e24
--- /dev/null
+++ b/www/packages/remark-rehype-plugins/src/add-url-to-relative-link.ts
@@ -0,0 +1,20 @@
+import { UnistNode, UnistTree } from "types"
+import { Transformer } from "unified"
+
+type Options = {
+ url: string
+}
+
+export function addUrlToRelativeLink(options: Options): Transformer {
+ return async (tree) => {
+ const { visit } = await import("unist-util-visit")
+
+ visit(tree as UnistTree, "link", (node: UnistNode) => {
+ if (!node.url || !node.url.startsWith("/")) {
+ return
+ }
+
+ node.url = `${options.url}${node.url}`
+ })
+ }
+}
diff --git a/www/packages/remark-rehype-plugins/src/broken-link-checker.ts b/www/packages/remark-rehype-plugins/src/broken-link-checker.ts
index 9307bc44b4..9264ae96e8 100644
--- a/www/packages/remark-rehype-plugins/src/broken-link-checker.ts
+++ b/www/packages/remark-rehype-plugins/src/broken-link-checker.ts
@@ -6,12 +6,12 @@ import type {
UnistNode,
UnistNodeWithData,
UnistTree,
-} from "./types/index.js"
+} from "types"
import type { VFile } from "vfile"
import { parseCrossProjectLink } from "./utils/cross-project-link-utils.js"
import { SlugChange } from "types"
import getAttribute from "./utils/get-attribute.js"
-import { estreeToJs } from "./utils/estree-to-js.js"
+import { estreeToJs } from "docs-utils"
import { performActionOnLiteral } from "./utils/perform-action-on-literal.js"
import { MD_LINK_REGEX } from "./constants.js"
diff --git a/www/packages/remark-rehype-plugins/src/cloudinary-img.ts b/www/packages/remark-rehype-plugins/src/cloudinary-img.ts
index b08cbf602e..ae06609d59 100644
--- a/www/packages/remark-rehype-plugins/src/cloudinary-img.ts
+++ b/www/packages/remark-rehype-plugins/src/cloudinary-img.ts
@@ -1,5 +1,5 @@
import type { Transformer } from "unified"
-import type { CloudinaryConfig, UnistNode, UnistTree } from "./types/index.js"
+import type { CloudinaryConfig, UnistNode, UnistTree } from "types"
const cloudinaryImageRegex =
/^https:\/\/res.cloudinary.com\/.*\/upload\/v[0-9]+\/(?.*)$/
diff --git a/www/packages/remark-rehype-plugins/src/cross-project-links.ts b/www/packages/remark-rehype-plugins/src/cross-project-links.ts
index 6adc53f277..bbaa6ec7c1 100644
--- a/www/packages/remark-rehype-plugins/src/cross-project-links.ts
+++ b/www/packages/remark-rehype-plugins/src/cross-project-links.ts
@@ -4,8 +4,8 @@ import type {
UnistNode,
UnistNodeWithData,
UnistTree,
-} from "./types/index.js"
-import { estreeToJs } from "./utils/estree-to-js.js"
+} from "types"
+import { estreeToJs } from "docs-utils"
import getAttribute from "./utils/get-attribute.js"
import { performActionOnLiteral } from "./utils/perform-action-on-literal.js"
@@ -48,6 +48,14 @@ function linkElmFixer(node: UnistNode, options: CrossProjectLinksOptions) {
node.properties.href = matchAndFixLinks(node.properties.href, options)
}
+function linkNodeFixer(node: UnistNode, options: CrossProjectLinksOptions) {
+ if (!node.url) {
+ return
+ }
+
+ node.url = matchAndFixLinks(node.url, options)
+}
+
function componentFixer(
node: UnistNodeWithData,
options: CrossProjectLinksOptions
@@ -128,20 +136,23 @@ export function crossProjectLinksPlugin(
visit(
tree as UnistTree,
- ["element", "mdxJsxFlowElement"],
+ ["element", "mdxJsxFlowElement", "link"],
(node: UnistNode) => {
const isComponent =
node.name && allowedComponentNames.includes(node.name)
- const isLink = node.tagName === "a" && node.properties?.href
- if (!isComponent && !isLink) {
+ const isLinkElm = node.tagName === "a" && node.properties?.href
+ const isLinkNode = node.type === "link" && node.url
+ if (!isComponent && !isLinkElm && !isLinkNode) {
return
}
if (isComponent) {
componentFixer(node as UnistNodeWithData, options)
+ } else if (isLinkElm) {
+ linkElmFixer(node, options)
+ } else {
+ linkNodeFixer(node, options)
}
-
- linkElmFixer(node, options)
}
)
}
diff --git a/www/packages/remark-rehype-plugins/src/index.ts b/www/packages/remark-rehype-plugins/src/index.ts
index 9e1ab7c242..904bddd118 100644
--- a/www/packages/remark-rehype-plugins/src/index.ts
+++ b/www/packages/remark-rehype-plugins/src/index.ts
@@ -1,3 +1,4 @@
+export * from "./add-url-to-relative-link.js"
export * from "./broken-link-checker.js"
export * from "./cloudinary-img.js"
export * from "./cross-project-links.js"
diff --git a/www/packages/remark-rehype-plugins/src/local-links.ts b/www/packages/remark-rehype-plugins/src/local-links.ts
index 357f95bc79..cd3263253c 100644
--- a/www/packages/remark-rehype-plugins/src/local-links.ts
+++ b/www/packages/remark-rehype-plugins/src/local-links.ts
@@ -1,6 +1,6 @@
import path from "path"
import type { Transformer } from "unified"
-import type { LocalLinkOptions, UnistNode, UnistTree } from "./types/index.js"
+import type { LocalLinkOptions, UnistNode, UnistTree } from "types"
import { fixLinkUtil } from "./index.js"
export function localLinksRehypePlugin(options: LocalLinkOptions): Transformer {
@@ -25,16 +25,28 @@ export function localLinksRehypePlugin(options: LocalLinkOptions): Transformer {
""
)
const appsPath = basePath || path.join(file.cwd, "app")
- visit(tree as UnistTree, "element", (node: UnistNode) => {
- if (node.tagName !== "a" || !node.properties?.href?.match(/page\.mdx?/)) {
- return
- }
+ visit(tree as UnistTree, ["element", "link"], (node: UnistNode) => {
+ if (node.tagName === "a") {
+ if (!node.properties?.href?.match(/page\.mdx?/)) {
+ return
+ }
- node.properties.href = fixLinkUtil({
- currentPageFilePath,
- linkedPath: node.properties.href,
- appsPath,
- })
+ node.properties.href = fixLinkUtil({
+ currentPageFilePath,
+ linkedPath: node.properties.href,
+ appsPath,
+ })
+ } else if (node.type === "link") {
+ if (!node.url?.match(/page\.mdx?/)) {
+ return
+ }
+
+ node.url = fixLinkUtil({
+ currentPageFilePath,
+ linkedPath: node.url,
+ appsPath,
+ })
+ }
})
}
}
diff --git a/www/packages/remark-rehype-plugins/src/page-number.ts b/www/packages/remark-rehype-plugins/src/page-number.ts
index cc16a4d978..32c5b77a36 100644
--- a/www/packages/remark-rehype-plugins/src/page-number.ts
+++ b/www/packages/remark-rehype-plugins/src/page-number.ts
@@ -1,7 +1,7 @@
import path from "path"
import type { Transformer } from "unified"
import type { RawSidebarItem } from "types"
-import type { UnistTree } from "./types/index.js"
+import type { UnistTree } from "types"
export function pageNumberRehypePlugin({
sidebar,
diff --git a/www/packages/remark-rehype-plugins/src/prerequisites-link-fixer.ts b/www/packages/remark-rehype-plugins/src/prerequisites-link-fixer.ts
index a9a220e564..a6f6213eaa 100644
--- a/www/packages/remark-rehype-plugins/src/prerequisites-link-fixer.ts
+++ b/www/packages/remark-rehype-plugins/src/prerequisites-link-fixer.ts
@@ -1,5 +1,5 @@
import { Transformer } from "unified"
-import { ComponentLinkFixerOptions } from "./types/index.js"
+import { ComponentLinkFixerOptions } from "types"
import { componentLinkFixer } from "./utils/component-link-fixer.js"
export function prerequisitesLinkFixerPlugin(
diff --git a/www/packages/remark-rehype-plugins/src/resolve-admonitions.ts b/www/packages/remark-rehype-plugins/src/resolve-admonitions.ts
index 1f579a160f..66e4456c39 100644
--- a/www/packages/remark-rehype-plugins/src/resolve-admonitions.ts
+++ b/www/packages/remark-rehype-plugins/src/resolve-admonitions.ts
@@ -1,5 +1,5 @@
import { Transformer } from "unified"
-import { UnistNode } from "./types/index.js"
+import { UnistNode } from "types"
const ALLOWED_NODE_NAMES = ["note", "tip", "info", "warning"]
diff --git a/www/packages/remark-rehype-plugins/src/type-list-link-fixer.ts b/www/packages/remark-rehype-plugins/src/type-list-link-fixer.ts
index 7724f47700..73cd640024 100644
--- a/www/packages/remark-rehype-plugins/src/type-list-link-fixer.ts
+++ b/www/packages/remark-rehype-plugins/src/type-list-link-fixer.ts
@@ -1,5 +1,5 @@
import { Transformer } from "unified"
-import { ComponentLinkFixerOptions } from "./types/index.js"
+import { ComponentLinkFixerOptions } from "types"
import { componentLinkFixer } from "./utils/component-link-fixer.js"
export function typeListLinkFixerPlugin(
diff --git a/www/packages/remark-rehype-plugins/src/utils/component-link-fixer.ts b/www/packages/remark-rehype-plugins/src/utils/component-link-fixer.ts
index 0a08c11fd7..be908e3e7b 100644
--- a/www/packages/remark-rehype-plugins/src/utils/component-link-fixer.ts
+++ b/www/packages/remark-rehype-plugins/src/utils/component-link-fixer.ts
@@ -1,10 +1,9 @@
import path from "path"
import { Transformer } from "unified"
-import { UnistNodeWithData, UnistTree } from "../types/index.js"
+import { UnistNodeWithData, UnistTree, ComponentLinkFixerOptions } from "types"
import { FixLinkOptions, fixLinkUtil } from "../index.js"
import getAttribute from "../utils/get-attribute.js"
-import { estreeToJs } from "../utils/estree-to-js.js"
-import { ComponentLinkFixerOptions } from "../types/index.js"
+import { estreeToJs } from "docs-utils"
import { performActionOnLiteral } from "./perform-action-on-literal.js"
import { MD_LINK_REGEX } from "../constants.js"
diff --git a/www/packages/remark-rehype-plugins/src/utils/get-attribute.ts b/www/packages/remark-rehype-plugins/src/utils/get-attribute.ts
index a50abe2fb9..20d4ca6100 100644
--- a/www/packages/remark-rehype-plugins/src/utils/get-attribute.ts
+++ b/www/packages/remark-rehype-plugins/src/utils/get-attribute.ts
@@ -1,4 +1,4 @@
-import { UnistNodeWithData } from "../types/index.js"
+import { UnistNodeWithData } from "types"
export default function getAttribute(
node: UnistNodeWithData,
diff --git a/www/packages/remark-rehype-plugins/src/utils/perform-action-on-literal.ts b/www/packages/remark-rehype-plugins/src/utils/perform-action-on-literal.ts
index 80a47c5fd5..5fee7746ad 100644
--- a/www/packages/remark-rehype-plugins/src/utils/perform-action-on-literal.ts
+++ b/www/packages/remark-rehype-plugins/src/utils/perform-action-on-literal.ts
@@ -1,8 +1,5 @@
-import { ExpressionJsVar, ExpressionJsVarLiteral } from "../types/index.js"
-import {
- isExpressionJsVarLiteral,
- isExpressionJsVarObj,
-} from "./expression-is-utils.js"
+import { ExpressionJsVar, ExpressionJsVarLiteral } from "types"
+import { isExpressionJsVarLiteral, isExpressionJsVarObj } from "docs-utils"
export const performActionOnLiteral = (
item: ExpressionJsVar[] | ExpressionJsVar,
diff --git a/www/packages/remark-rehype-plugins/src/workflow-diagram-link-fixer.ts b/www/packages/remark-rehype-plugins/src/workflow-diagram-link-fixer.ts
index 534822552e..97ca819a9d 100644
--- a/www/packages/remark-rehype-plugins/src/workflow-diagram-link-fixer.ts
+++ b/www/packages/remark-rehype-plugins/src/workflow-diagram-link-fixer.ts
@@ -1,5 +1,5 @@
import { Transformer } from "unified"
-import { ComponentLinkFixerOptions } from "./types/index.js"
+import { ComponentLinkFixerOptions } from "types"
import { componentLinkFixer } from "./utils/component-link-fixer.js"
export function workflowDiagramLinkFixerPlugin(
diff --git a/www/packages/types/src/index.ts b/www/packages/types/src/index.ts
index 069be48cdc..9717696f3f 100644
--- a/www/packages/types/src/index.ts
+++ b/www/packages/types/src/index.ts
@@ -5,6 +5,7 @@ export * from "./frontmatter.js"
export * from "./general.js"
export * from "./menu.js"
export * from "./navigation.js"
+export * from "./remark-rehype.js"
export * from "./navigation-dropdown.js"
export * from "./sidebar.js"
export * from "./tags.js"
diff --git a/www/packages/remark-rehype-plugins/src/types/index.ts b/www/packages/types/src/remark-rehype.ts
similarity index 97%
rename from www/packages/remark-rehype-plugins/src/types/index.ts
rename to www/packages/types/src/remark-rehype.ts
index 7a0543b2c9..752db4b298 100644
--- a/www/packages/remark-rehype-plugins/src/types/index.ts
+++ b/www/packages/types/src/remark-rehype.ts
@@ -14,6 +14,10 @@ export interface UnistNode extends Node {
type?: string
}[]
children?: UnistNode[]
+ ordered?: boolean
+ url?: string
+ spread?: boolean
+ depth?: number
}
export type ArrayExpression = {
diff --git a/www/yarn.lock b/www/yarn.lock
index 55bd29e05e..fabe4043e4 100644
--- a/www/yarn.lock
+++ b/www/yarn.lock
@@ -1583,7 +1583,7 @@ __metadata:
languageName: node
linkType: hard
-"@mdx-js/mdx@npm:^3.0.1":
+"@mdx-js/mdx@npm:^3.0.1, @mdx-js/mdx@npm:^3.1.0":
version: 3.1.0
resolution: "@mdx-js/mdx@npm:3.1.0"
dependencies:
@@ -7196,8 +7196,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "docs-utils@workspace:packages/docs-utils"
dependencies:
+ "@mdx-js/mdx": ^3.1.0
"@types/node": ^20.11.20
remark-frontmatter: ^5.0.0
+ remark-mdx: ^3.1.0
remark-parse: ^11.0.0
remark-stringify: ^11.0.0
rimraf: ^5.0.5
@@ -13297,6 +13299,16 @@ __metadata:
languageName: node
linkType: hard
+"remark-mdx@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "remark-mdx@npm:3.1.0"
+ dependencies:
+ mdast-util-mdx: ^3.0.0
+ micromark-extension-mdxjs: ^3.0.0
+ checksum: 247800fa8561624bdca5776457c5965d99e5e60080e80262c600fe12ddd573862e029e39349e1e36e4c3bf79c8e571ecf4d3d2d8c13485b758391fb500e24a1a
+ languageName: node
+ linkType: hard
+
"remark-parse@npm:^10.0.0, remark-parse@npm:^10.0.2":
version: 10.0.2
resolution: "remark-parse@npm:10.0.2"