Files
supabase/apps/design-system/lib/rehype-component.ts
Gildas Garcia 86a3f8b03d chore: upgrade to react-19 (#45886)
- Most changes are related to either types or `useRef` usages (it now
requires an initial value).
- also updated `vaul` to its latest version and haven't noticed any
change ([design-system
demo](https://design-system-git-react-19-supabase.vercel.app/design-system/docs/components/drawer))

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Upgraded workspace to React 19.

* **Bug Fixes**
* Improved null-safety and ref handling across editors, UI components,
shortcuts, and markdown/image rendering to reduce runtime errors.
* Safer event/timeout/interval cleanup and more robust command/context
handling.

* **Chores**
  * Bumped vaul dependency versions.

* **Documentation**
* Type and TypeScript accuracy improvements for clearer developer
feedback.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45886)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 16:04:41 +02:00

334 lines
10 KiB
TypeScript

import fs from 'fs'
import path from 'path'
// import { Column, IColumnProps } from './sample-component'
import React from 'react'
import { u } from 'unist-builder'
import { visit } from 'unist-util-visit'
import { Index } from '../__registry__'
import { styles } from '../registry/styles'
import { UnistNode, UnistTree } from '@/types/unist'
export function rehypeComponent() {
return async (tree: UnistTree) => {
visit(tree, (node: UnistNode) => {
// src prop overrides both name and fileName.
const { value: srcPath, name: componentName } =
(getNodeAttributeByName(node, 'src') as {
name: string
value?: string
type?: string
}) || {}
if (node.name === 'ComponentSource') {
// console.log('DO NOT USE THIS COMPONENT')
// This component should not be in use!
// console.log('node', node)
const name = getNodeAttributeByName(node, 'name')?.value as string
const fileName = getNodeAttributeByName(node, 'fileName')?.value as string | undefined
if (!name && !srcPath) {
return null
}
try {
for (const style of styles) {
let src: string
if (srcPath) {
src = srcPath
} else {
const component = Index[style.name][name]
// console.log('got to ELSE STATEMENT')
// console.log('filename', fileName)
// console.log('name', name)
src = fileName
? component.files.find((file: string) => {
return file.endsWith(`${fileName}.tsx`) || file.endsWith(`${fileName}.ts`)
}) || component.files[0]
: component.files[0]
// console.log('got to END of ELSE STATEMENT')
}
// Read the source file.
const filePath = path.join(process.cwd(), src)
let source = fs.readFileSync(filePath, 'utf8')
// Replace imports.
// TODO: Use @swc/core and a visitor to replace this.
// For now a simple regex should do.
// source = source.replaceAll(`@/registry/${style.name}/`, '@/components/') // COMMENT THEE OUT
// source = source.replaceAll('export default', 'export')
// Add code as children so that rehype can take over at build time.
node.children?.push(
u('element', {
tagName: 'pre',
properties: {
__src__: src,
__style__: style.name,
},
attributes: [
{
name: 'styleName',
type: 'mdxJsxAttribute',
value: style.name,
},
],
children: [
u('element', {
tagName: 'code',
properties: {
className: ['language-tsx'],
},
children: [
{
type: 'text',
value: source,
},
],
}),
],
})
)
}
} catch (error) {
console.error(error)
}
}
if (node.name === 'ComponentPreview') {
const name = getNodeAttributeByName(node, 'name')?.value as string
if (!name) {
return null
}
try {
for (const style of styles) {
const component = Index[style.name][name]
// console.log('GOT HERE')
const src = component.files[0]
// Read the source file.
const filePath = path.join(process.cwd(), src)
let source = fs.readFileSync(filePath, 'utf8')
// Replace imports.
// TODO: Use @swc/core and a visitor to replace this.
// For now a simple regex should do.
source = source.replaceAll(`@/registry/${style.name}/`, '@/components/')
source = source.replaceAll('export default', 'export')
// Add code as children so that rehype can take over at build time.
node.children?.push(
u('element', {
tagName: 'pre',
properties: {
__src__: src,
},
children: [
u('element', {
tagName: 'code',
properties: {
className: ['language-tsx'],
},
children: [
{
type: 'text',
value: source,
},
],
}),
],
})
)
}
} catch (error) {
console.error(error)
}
}
if (node.name === 'CodeFragment') {
const name = getNodeAttributeByName(node, 'name')?.value as string
if (!name) {
return null
}
try {
for (const style of styles) {
const component = Index[style.name][name]
// console.log('GOT HERE')
const src = component.files[0]
// Read the source file.
const filePath = path.join(process.cwd(), src)
let source = fs.readFileSync(filePath, 'utf8')
// Replace imports.
// TODO: Use @swc/core and a visitor to replace this.
// For now a simple regex should do.
source = source.replaceAll(`@/registry/${style.name}/`, '@/components/')
source = source.replaceAll('export default', 'export')
// Add code as children so that rehype can take over at build time.
node.children?.push(
u('element', {
tagName: 'pre',
properties: {
__src__: src,
},
children: [
u('element', {
tagName: 'code',
properties: {
className: ['language-tsx'],
},
children: [
{
type: 'text',
value: source,
},
],
}),
],
})
)
}
} catch (error) {
console.error(error)
}
}
// if (node.name === "ComponentExample") {
// const source = getComponentSourceFileContent(node)
// if (!source) {
// return
// }
// // Replace the Example component with a pre element.
// node.children?.push(
// u("element", {
// tagName: "pre",
// properties: {
// __src__: src,
// },
// children: [
// u("element", {
// tagName: "code",
// properties: {
// className: ["language-tsx"],
// },
// children: [
// {
// type: "text",
// value: source,
// },
// ],
// }),
// ],
// })
// )
// const extractClassname = getNodeAttributeByName(
// node,
// "extractClassname"
// )
// if (
// extractClassname &&
// typeof extractClassname.value !== "undefined" &&
// extractClassname.value !== "false"
// ) {
// // Extract className from string
// // TODO: Use @swc/core and a visitor to extract this.
// // For now, a simple regex should do.
// const values = source.match(/className="(.*)"/)
// const className = values ? values[1] : ""
// // Add the className as a jsx prop so we can pass it to the copy button.
// node.attributes?.push({
// name: "extractedClassNames",
// type: "mdxJsxAttribute",
// value: className,
// })
// // Add a pre element with the className only.
// node.children?.push(
// u("element", {
// tagName: "pre",
// properties: {},
// children: [
// u("element", {
// tagName: "code",
// properties: {
// className: ["language-tsx"],
// },
// children: [
// {
// type: "text",
// value: className,
// },
// ],
// }),
// ],
// })
// )
// }
// }
// if (node.name === "ComponentSource") {
// const source = getComponentSourceFileContent(node)
// if (!source) {
// return
// }
// // Replace the Source component with a pre element.
// node.children?.push(
// u("element", {
// tagName: "pre",
// properties: {
// __src__: src,
// },
// children: [
// u("element", {
// tagName: "code",
// properties: {
// className: ["language-tsx"],
// },
// children: [
// {
// type: "text",
// value: source,
// },
// ],
// }),
// ],
// })
// )
// }
})
}
}
function getNodeAttributeByName(node: UnistNode, name: string) {
return node.attributes?.find((attribute) => attribute.name === name)
}
function getComponentSourceFileContent(node: UnistNode) {
const src = getNodeAttributeByName(node, 'src')?.value as string
if (!src) {
return null
}
// Read the source file.
const filePath = path.join(process.cwd(), src)
console.log('filePath', filePath)
const source = fs.readFileSync(filePath, 'utf8')
return source
}