Files
supabase/apps/ui-library/velite.config.js
Anthony Lio 0fe320e5e3 chore: shiki velite migration (#51579)
## Problem

design-system, ui-library and learn highlight code through
`@shikijs/compat` and `rehype-pretty-code` 0.9, which both predate shiki
1

compat has no v4 release, so these apps were stuck on shiki 1.6 and
couldn't pick up the injection fix from the previous pr

## Solution

- drops `@shikijs/compat`
- moves all three apps to `rehype-pretty-code`

<!--
## Preview links

If relevant, include links to changed pages for easy review access.

Copy the preview base URL from the Vercel bot comment on this PR. Use
the following table as an example template.

| Site | Live | Preview | Search for |
| -------------- |
-------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
| ----------------------------- |
| WWW | [/blog/your-post](https://supabase.com/blog/your-post) |
[/blog/your-post](https://zone-www-dot-com-git-branch-name-supabase.vercel.app/blog/your-post)
| unique phrase from the change |
| Docs |
[/docs/guides/your-page](https://supabase.com/docs/guides/your-page) |
[/docs/guides/your-page](https://docs-git-branch-name-supabase.vercel.app/docs/guides/your-page)
| unique phrase from the change |
| Studio | [/dashboard](https://supabase.com/dashboard) |
[/dashboard](https://studio-git-branch-name-supabase.vercel.app/dashboard)
| unique phrase from the change |
| Design system | [/design-system](https://supabase.com/design-system) |
[/design-system](https://design-system-git-branch-name-supabase.vercel.app/design-system)
| unique phrase from the change |
| UI library | [/library](https://supabase.com/library) |
[/library](https://ui-library-git-branch-name-supabase.vercel.app/library)
| unique phrase from the change |
| Knowledge base |
[/kb/guides/your-page](https://supabase.com/kb/guides/your-page) |
[/kb/guides/your-page](https://kb-git-branch-name-supabase.vercel.app/kb/guides/your-page)
| unique phrase from the change |
-->

<!-- ## Additional context

Optionally add any other context or screenshots.

-->

## Review instructions

1. open
[/design-system/docs/accessibility](https://design-system-gd7xzujta-supabase.vercel.app/design-system/docs/accessibility)
on the preview and on production side by side _ highlighted lines should
keep the left border and background, and line numbers should line up
2. open
[/design-system/docs/components/chart](https://design-system-gd7xzujta-supabase.vercel.app/design-system/docs/components/chart)
_ highlighted words should have the gray pill, with no yellow background
or black text + code blocks with titles should still show the title
above the block
3. toggle light and dark mode on both pages _ token colors and the block
background should match production
4. open [ui-library
preview](https://ui-library-hmuv1teie-supabase.vercel.app/library/docs/starters/nextjs-starter)
and do a copy check
5. open a learn lesson with code `/foundations/authentication` and
compare it with production.

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
2026-10-14 12:46:03 +03:00

170 lines
5.4 KiB
JavaScript

import { readFile } from 'node:fs/promises'
import path from 'path'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypePrettyCode from 'rehype-pretty-code'
import rehypeSlug from 'rehype-slug'
import { codeImport } from 'remark-code-import'
import remarkGfm from 'remark-gfm'
import { visit } from 'unist-util-visit'
import { defineConfig, s } from 'velite'
const codeTheme = JSON.parse(
await readFile(path.join(process.cwd(), 'lib/themes/supabase-2.json'), 'utf8')
)
const LinksProperties = s.object({
doc: s.string().optional(),
api: s.string().optional(),
})
const NestedProperties = s.object({
radix: s.boolean().optional(),
shadcn: s.boolean().optional(),
vaul: s.boolean().optional(),
inputOtp: s.boolean().optional(),
reactAccessibleTreeview: s.boolean().optional(),
})
// The MDX is the only reliable source for which registry item v0 can open: doc
// slugs don't map onto registry names, and pages that install a client first
// list it as an extra BlockItem that opts out. Take the first item still opted in.
const getV0RegistryName = (raw) => {
for (const [, attributes] of raw.matchAll(/<BlockItem\b([^>]*?)\/?>/g)) {
if (/showOpenInV0=\{false\}/.test(attributes)) continue
const name = attributes.match(/name="([^"]+)"/)
if (name) return name[1]
}
return undefined
}
const docs = s
.object({
title: s.string(),
description: s.string(),
preview: s.mdx({ copyLinkedFiles: false }).optional(),
published: s.boolean().default(true),
links: LinksProperties.optional(),
featured: s.boolean().default(false),
component: s.boolean().default(false),
fragment: s.boolean().default(false),
source: NestedProperties.optional(),
// mirrors contentlayer2's `_raw.flattenedPath`: file path relative to the
// content dir, extension stripped, trailing `/index` dropped.
path: s.path(),
raw: s.raw(),
// internal doc cross-links (e.g. `[Button](components/button)`) aren't
// real files on disk — disable Velite's default asset-copying behavior,
// which otherwise treats every relative link as a local file to copy.
code: s.mdx({ copyLinkedFiles: false }),
})
.transform(({ path: flattenedPath, ...data }) => ({
...data,
slug: `/${flattenedPath}`,
slugAsParams: flattenedPath.split('/').slice(1).join('/'),
v0Name: getV0RegistryName(data.raw),
}))
export default defineConfig({
root: './content',
output: {
clean: true,
},
collections: {
allDocs: {
name: 'Doc',
pattern: 'docs/**/*.mdx',
schema: docs,
},
},
mdx: {
remarkPlugins: [remarkGfm, codeImport],
rehypePlugins: [
rehypeSlug,
() => (tree) => {
visit(tree, (node) => {
if (node?.type === 'element' && node?.tagName === 'pre') {
const [codeEl] = node.children
if (codeEl.tagName !== 'code') {
return
}
if (codeEl.data?.meta) {
// Extract event from meta and pass it down the tree.
const regex = /event="([^"]*)"/
const match = codeEl.data?.meta.match(regex)
if (match) {
node.__event__ = match ? match[1] : null
codeEl.data.meta = codeEl.data.meta.replace(regex, '')
}
}
node.__rawString__ = codeEl.children?.[0].value
node.__src__ = node.properties?.__src__
node.__style__ = node.properties?.__style__
}
})
},
[
rehypePrettyCode,
{
// plugin shares one cached highlighter per theme, lazy language loading needs shiki >= 4.5 (shikijs/shiki#1318)
theme: codeTheme,
keepBackground: false,
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and allow empty
// lines to be copy/pasted
if (node.children.length === 0) {
node.children = [{ type: 'text', value: ' ' }]
}
},
onVisitHighlightedLine(node) {
// the plugin strips the `line` class first, leaving className undefined
node.properties.className = [...(node.properties.className ?? []), 'line--highlighted']
},
onVisitHighlightedChars(node) {
node.properties.className = ['word--highlighted']
},
},
],
() => (tree) => {
visit(tree, (node) => {
if (node?.type === 'element' && node?.tagName === 'figure') {
if (!('data-rehype-pretty-code-figure' in node.properties)) {
return
}
const preElement = node.children.at(-1)
if (preElement.tagName !== 'pre') {
return
}
preElement.properties['__withMeta__'] = node.children.at(0).tagName === 'figcaption'
preElement.properties['__rawString__'] = node.__rawString__
if (node.__src__) {
preElement.properties['__src__'] = node.__src__
}
if (node.__event__) {
preElement.properties['__event__'] = node.__event__
}
if (node.__style__) {
preElement.properties['__style__'] = node.__style__
}
}
})
},
[
rehypeAutolinkHeadings,
{
properties: {
className: ['subheading-anchor'],
ariaLabel: 'Link to section',
},
},
],
],
},
})