mirror of
https://github.com/supabase/supabase.git
synced 2026-05-07 23:19:23 +08:00
* change database to pagelayout * page components * design system documentation * reduce preview width * revert studio * functions apply page * layout * interfaces * remove demos * pckage * format * format * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * build registry * pckg * build script to allow index files * switch exports * fixes * gen exports * remove invalid prop * fix typecheck * fix prettier * Revert "remove invalid prop" This reverts commit118ecd40c5. * Revert "fix typecheck" This reverts commit0e1ec8fb61. * Revert "fix prettier" This reverts commit2f25ed695c. * pretter * fresh pnpm i * Update pnpm lock * fix import * revert * Update packages/ui-patterns/src/PageContainer/index.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update packages/ui-patterns/src/PageHeader/index.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update packages/ui-patterns/src/PageSection/index.tsx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * ts fix * Update apps/design-system/content/docs/ui-patterns/page.mdx Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> * change to layouts and examples --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com> Co-authored-by: Joshen Lim <joshenlimek@gmail.com> Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
341 lines
11 KiB
TypeScript
341 lines
11 KiB
TypeScript
// @sts-nocheck
|
|
import { existsSync, promises as fs } from 'fs'
|
|
import { tmpdir } from 'os'
|
|
import path from 'path'
|
|
import { cwd } from 'process'
|
|
import { rimraf } from 'rimraf'
|
|
import { Project, ScriptKind, SyntaxKind } from 'ts-morph'
|
|
|
|
import { registry } from '../registry/registry'
|
|
import { Registry, registrySchema } from '../registry/schema'
|
|
import { styles } from '../registry/styles'
|
|
|
|
const REGISTRY_PATH = path.join(process.cwd(), 'public/registry')
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Build __registry__/index.tsx.
|
|
// ----------------------------------------------------------------------------
|
|
async function buildRegistry(registry: Registry) {
|
|
const project = new Project({
|
|
compilerOptions: {},
|
|
})
|
|
|
|
async function createTempSourceFile(filename: string) {
|
|
const dir = await fs.mkdtemp(path.join(tmpdir(), 'shadcn-'))
|
|
return path.join(dir, filename)
|
|
}
|
|
|
|
let index = `// @ts-nocheck
|
|
// This file is autogenerated by scripts/build-registry.ts
|
|
// Do not edit this file directly.
|
|
import * as React from "react"
|
|
|
|
export const Index: Record<string, any> = {
|
|
`
|
|
|
|
for (const style of styles) {
|
|
index += ` "${style.name}": {`
|
|
|
|
// Build style index.
|
|
for (const item of registry) {
|
|
const resolveFiles = item.files.map((file) => `registry/${style.name}/${file}`)
|
|
const type = item.type.split(':')[1]
|
|
let sourceFilename = ''
|
|
|
|
let chunks: any = []
|
|
if (item.type === 'components:block') {
|
|
const file = resolveFiles[0]
|
|
const filename = path.basename(file)
|
|
const raw = await fs.readFile(file, 'utf8')
|
|
const tempFile = await createTempSourceFile(filename)
|
|
const sourceFile = project.createSourceFile(tempFile, raw, {
|
|
scriptKind: ScriptKind.TSX,
|
|
})
|
|
|
|
// Find all imports.
|
|
const imports = new Map<
|
|
string,
|
|
{
|
|
module: string
|
|
text: string
|
|
isDefault?: boolean
|
|
}
|
|
>()
|
|
sourceFile.getImportDeclarations().forEach((node) => {
|
|
const module = node.getModuleSpecifier().getLiteralValue()
|
|
node.getNamedImports().forEach((item) => {
|
|
imports.set(item.getText(), {
|
|
module,
|
|
text: node.getText(),
|
|
})
|
|
})
|
|
|
|
const defaultImport = node.getDefaultImport()
|
|
if (defaultImport) {
|
|
imports.set(defaultImport.getText(), {
|
|
module,
|
|
text: defaultImport.getText(),
|
|
isDefault: true,
|
|
})
|
|
}
|
|
})
|
|
|
|
// Find all opening tags with x-chunk attribute.
|
|
const components = sourceFile
|
|
.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
|
|
.filter((node) => {
|
|
return node.getAttribute('x-chunk') !== undefined
|
|
})
|
|
|
|
chunks = await Promise.all(
|
|
components.map(async (component, index) => {
|
|
const chunkName = `${item.name}-chunk-${index}`
|
|
|
|
// Get the value of x-chunk attribute.
|
|
const attr = component
|
|
.getAttributeOrThrow('x-chunk')
|
|
.asKindOrThrow(SyntaxKind.JsxAttribute)
|
|
|
|
const description = attr
|
|
.getInitializerOrThrow()
|
|
.asKindOrThrow(SyntaxKind.StringLiteral)
|
|
.getLiteralValue()
|
|
|
|
// Delete the x-chunk attribute.
|
|
attr.remove()
|
|
|
|
// Add a new attribute to the component.
|
|
component.addAttribute({
|
|
name: 'x-chunk',
|
|
initializer: `"${chunkName}"`,
|
|
})
|
|
|
|
// Get the value of x-chunk-container attribute.
|
|
const containerAttr = component
|
|
.getAttribute('x-chunk-container')
|
|
?.asKindOrThrow(SyntaxKind.JsxAttribute)
|
|
|
|
const containerClassName = containerAttr
|
|
?.getInitializer()
|
|
?.asKindOrThrow(SyntaxKind.StringLiteral)
|
|
.getLiteralValue()
|
|
|
|
containerAttr?.remove()
|
|
|
|
const parentJsxElement = component.getParentIfKindOrThrow(SyntaxKind.JsxElement)
|
|
|
|
// Find all opening tags on component.
|
|
const children = parentJsxElement
|
|
.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
|
|
.map((node) => {
|
|
return node.getTagNameNode().getText()
|
|
})
|
|
.concat(
|
|
parentJsxElement
|
|
.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)
|
|
.map((node) => {
|
|
return node.getTagNameNode().getText()
|
|
})
|
|
)
|
|
|
|
const componentImports = new Map<string, string | string[] | Set<string>>()
|
|
children.forEach((child) => {
|
|
const importLine = imports.get(child)
|
|
if (importLine) {
|
|
const imports = componentImports.get(importLine.module) || []
|
|
|
|
const newImports = importLine.isDefault
|
|
? importLine.text
|
|
: new Set([...imports, child])
|
|
|
|
componentImports.set(
|
|
importLine.module,
|
|
importLine?.isDefault ? newImports : Array.from(newImports)
|
|
)
|
|
}
|
|
})
|
|
|
|
const componnetImportLines = Array.from(componentImports.keys()).map((key) => {
|
|
const values = componentImports.get(key)
|
|
const specifier = Array.isArray(values) ? `{${values.join(',')}}` : values
|
|
|
|
return `import ${specifier} from "${key}"`
|
|
})
|
|
|
|
const code = `
|
|
${componnetImportLines.join('\n')}
|
|
|
|
export default function Component() {
|
|
return (${parentJsxElement.getText()})
|
|
}`
|
|
|
|
const targetFile = file.replace(item.name, `${chunkName}`)
|
|
const targetFilePath = path.join(
|
|
cwd(),
|
|
`registry/${style.name}/${type}/${chunkName}.tsx`
|
|
)
|
|
|
|
// Write component file.
|
|
rimraf.sync(targetFilePath)
|
|
await fs.writeFile(targetFilePath, code, 'utf8')
|
|
|
|
return {
|
|
name: chunkName,
|
|
description,
|
|
component: `React.lazy(() => import("@/registry/${style.name}/${type}/${chunkName}")),`,
|
|
file: targetFile,
|
|
container: {
|
|
className: containerClassName,
|
|
},
|
|
}
|
|
})
|
|
)
|
|
|
|
// // Write the source file for blocks only.
|
|
sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`
|
|
const sourcePath = path.join(process.cwd(), sourceFilename)
|
|
if (!existsSync(sourcePath)) {
|
|
await fs.mkdir(sourcePath, { recursive: true })
|
|
}
|
|
|
|
rimraf.sync(sourcePath)
|
|
await fs.writeFile(sourcePath, sourceFile.getText())
|
|
}
|
|
|
|
// console.log('type', type)
|
|
|
|
// console.log('item', item)
|
|
|
|
let packagePath = ''
|
|
let componentImportPath = ''
|
|
if (type === 'ui') {
|
|
packagePath = `../../packages/ui/src/components/shadcn/ui`
|
|
componentImportPath = `@/${packagePath}/${item.name}`
|
|
}
|
|
if (type === 'fragment') {
|
|
packagePath = `../../packages/ui-patterns/src${item.optionalPath}`
|
|
// Check if the file is index.tsx - if so, don't append the item name
|
|
const isIndexFile = item.files.some((file) => {
|
|
const basename = path.basename(file)
|
|
return basename === 'index.tsx' || basename === 'index.ts'
|
|
})
|
|
componentImportPath = isIndexFile
|
|
? `@/${packagePath}`
|
|
: `@/${packagePath}/${item.name}`
|
|
}
|
|
if (type === 'example') {
|
|
packagePath = `registry/${style.name}/${type}`
|
|
componentImportPath = `@/${packagePath}/${item.name}`
|
|
}
|
|
if (type === 'block') {
|
|
packagePath = `registry/${style.name}/${type}`
|
|
componentImportPath = `@/${packagePath}/${item.name}`
|
|
}
|
|
|
|
index += `
|
|
"${item.name}": {
|
|
name: "${item.name}",
|
|
type: "${item.type}",
|
|
registryDependencies: ${JSON.stringify(item.registryDependencies)},
|
|
component: React.lazy(() => import("${componentImportPath}")),
|
|
source: "${sourceFilename}",
|
|
files: [${resolveFiles.map((file) => `"${file}"`)}],
|
|
category: "${item.category}",
|
|
subcategory: "${item.subcategory}",
|
|
chunks: [${chunks.map(
|
|
(chunk) => `{
|
|
name: "${chunk.name}",
|
|
description: "${chunk.description}",
|
|
component: ${chunk.component}
|
|
file: "${chunk.file}",
|
|
container: {
|
|
className: "${chunk.container.className}"
|
|
}
|
|
}`
|
|
)}]
|
|
},`
|
|
}
|
|
|
|
index += `
|
|
},`
|
|
}
|
|
|
|
index += `
|
|
}
|
|
`
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Build registry/index.json.
|
|
// ----------------------------------------------------------------------------
|
|
// const names = registry.filter((item) => item.type === 'components:ui')
|
|
// const registryJson = JSON.stringify(names, null, 2)
|
|
// rimraf.sync(path.join(REGISTRY_PATH, 'index.json'))
|
|
// await fs.writeFile(path.join(REGISTRY_PATH, 'index.json'), registryJson, 'utf8')
|
|
|
|
// Write style index.
|
|
rimraf.sync(path.join(process.cwd(), '__registry__/index.tsx'))
|
|
await fs.writeFile(path.join(process.cwd(), '__registry__/index.tsx'), index)
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Build registry/styles/[style]/[name].json.
|
|
// ----------------------------------------------------------------------------
|
|
// async function buildStyles(registry: Registry) {
|
|
// for (const style of styles) {
|
|
// const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)
|
|
|
|
// console.log('targetPath', targetPath)
|
|
|
|
// // Create directory if it doesn't exist.
|
|
// if (!existsSync(targetPath)) {
|
|
// await fs.mkdir(targetPath, { recursive: true })
|
|
// }
|
|
|
|
// for (const item of registry) {
|
|
// if (item.type !== 'components:ui') {
|
|
// continue
|
|
// }
|
|
|
|
// const files = item.files?.map((file) => {
|
|
// console.log('path', path.join(process.cwd(), 'registry', style.name, file))
|
|
|
|
// const content = readFileSync(path.join(process.cwd(), 'registry', style.name, file), 'utf8')
|
|
|
|
// return {
|
|
// name: basename(file),
|
|
// content,
|
|
// }
|
|
// })
|
|
|
|
// const payload = {
|
|
// ...item,
|
|
// files,
|
|
// }
|
|
|
|
// await fs.writeFile(
|
|
// path.join(targetPath, `${item.name}.json`),
|
|
// JSON.stringify(payload, null, 2),
|
|
// 'utf8'
|
|
// )
|
|
// }
|
|
// }
|
|
|
|
try {
|
|
console.log('🚀 Building registry...')
|
|
|
|
const result = registrySchema.safeParse(registry)
|
|
|
|
if (!result.success) {
|
|
console.error(result.error)
|
|
process.exit(1)
|
|
}
|
|
|
|
await buildRegistry(result.data)
|
|
// await buildStyles(result.data)
|
|
// await buildThemes()
|
|
|
|
console.log('✅ Done!')
|
|
} catch (error) {
|
|
console.error(error)
|
|
process.exit(1)
|
|
} |