tldraw/apps/examples/vite.config.ts
Taha f25f92a46d
Grouping examples into categories (#2585)
This PR adds collapsible groups to the examples app.

it's not finished, but I'd like a review before continuing as I've made
a few decisions I'd like feedback on. I'd like to make a separate issue
for abstracting the accordion component, as I wasn't sure how I would do
it and I thought it would be best to prioritise the functionality first.
Especially considering there are more pressing issues to be getting on
with.

### Change Type

- [ ] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [x] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Release Notes

- Add collapsible categories to the examples app

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
2024-01-29 10:04:41 +00:00

108 lines
3.3 KiB
TypeScript

import react from '@vitejs/plugin-react'
import path from 'path'
import { PluginOption, defineConfig } from 'vite'
export default defineConfig({
plugins: [react(), exampleReadmePlugin()],
root: path.join(__dirname, 'src'),
publicDir: path.join(__dirname, 'public'),
build: {
outDir: path.join(__dirname, 'dist'),
assetsInlineLimit: 0,
},
server: {
port: 5420,
},
clearScreen: false,
optimizeDeps: {
exclude: ['@tldraw/assets'],
},
define: {
'process.env.TLDRAW_ENV': JSON.stringify(process.env.VERCEL_ENV ?? 'development'),
},
})
function exampleReadmePlugin(): PluginOption {
return {
name: 'example-readme',
async transform(src, id) {
const match = id.match(/examples\/src\/examples\/(.*)\/README.md$/)
if (!match) return
const remark = (await import('remark')).remark
const remarkFrontmatter = (await import('remark-frontmatter')).default
const remarkHtml = (await import('remark-html')).default
const matter = (await import('vfile-matter')).matter
const file = await remark()
.use(remarkFrontmatter)
.use(remarkHtml)
.use(() => (_, file) => matter(file))
.process(src)
const frontmatter = parseFrontMatter(file.data.matter, id)
const separator = '\n<hr>\n'
const parts = String(file).split(separator)
const description = parts[0]
const details = parts.slice(1).join(separator)
const path = `/${match[1]}`
const codeUrl = `https://github.com/tldraw/tldraw/tree/main/apps/examples/src/examples${path}`
const result = [
`export const title = ${JSON.stringify(frontmatter.title)};`,
`export const priority = ${JSON.stringify(frontmatter.priority)};`,
`export const category = ${JSON.stringify(frontmatter.category)};`,
`export const hide = ${JSON.stringify(frontmatter.hide)};`,
`export const description = ${JSON.stringify(description)};`,
`export const details = ${JSON.stringify(details)};`,
`export const codeUrl = ${JSON.stringify(codeUrl)};`,
`export const path = ${JSON.stringify(path)};`,
`export const componentFile = ${JSON.stringify(frontmatter.component)};`,
`import {lazy} from 'react';`,
`export const loadComponent = async () => {`,
` return (await import(${JSON.stringify(frontmatter.component)})).default;`,
`};`,
]
return result.join('\n')
},
}
}
function parseFrontMatter(data: unknown, fileName: string) {
if (!data || typeof data !== 'object') {
throw new Error(`Frontmatter missing in ${fileName}`)
}
if (!('title' in data && typeof data.title === 'string')) {
throw new Error(`Frontmatter key 'title' must be string in ${fileName}`)
}
if (!('component' in data && typeof data.component === 'string')) {
throw new Error(`Frontmatter key 'component' must be string in ${fileName}`)
}
const priority = 'priority' in data ? data.priority : null
if (typeof priority !== 'number') {
throw new Error(`Frontmatter key 'priority' must be number in ${fileName}`)
}
const category = 'category' in data ? data.category : null
if (typeof category !== 'string') {
throw new Error(`Frontmatter key 'category' must be string in ${fileName}`)
}
const hide = 'hide' in data ? data.hide : false
if (hide !== false && hide !== true) {
throw new Error(`Frontmatter key 'hide' must be boolean in ${fileName}`)
}
return {
title: data.title,
component: data.component,
priority,
category,
hide,
}
}