mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 19:35:06 +03:00
chore: Define new structure to support other definitions
This commit is contained in:
1 parent
fe40cae142
commit
e5dc0db5e6
5 files changed
+135
-68355
No files matched your search
@@ -36,7 +36,7 @@ export type LibKey = keyof typeof libs
|
||||
*/
|
||||
async function getReferenceContent(library: string, version: string | undefined) {
|
||||
const libKey = getLibKey(library, version)
|
||||
const filePath = join(REFERENCE_DIRECTORY, `${libKey}.mdx`)
|
||||
const filePath = join(REFERENCE_DIRECTORY, libKey, 'index.mdx')
|
||||
const fileContent = await fs.readFile(filePath, 'utf-8')
|
||||
const { data: meta, content } = matter(fileContent)
|
||||
return { meta, content } as { content: string; meta: GuideFrontmatter }
|
||||
@@ -44,7 +44,7 @@ async function getReferenceContent(library: string, version: string | undefined)
|
||||
|
||||
async function getReferenceSections(library: string, version: string | undefined) {
|
||||
const libKey = getLibKey(library, version)
|
||||
const filePath = join(REFERENCE_DIRECTORY, `${libKey}.sections.json`)
|
||||
const filePath = join(REFERENCE_DIRECTORY, libKey, 'sections.json')
|
||||
const fileContent = await fs.readFile(filePath, 'utf-8')
|
||||
return JSON.parse(fileContent) as AbbrevApiReferenceSection[]
|
||||
}
|
||||
@@ -79,7 +79,7 @@ async function ReferencePage() {
|
||||
const { library, version } = params
|
||||
const libKey = `${library}${version ? `-${version}` : ''}` as LibKey
|
||||
const { name, icon, currentVersion, isLatestVersion } = libs[libKey]
|
||||
// const sections = await getReferenceSections(library, version)
|
||||
const sections = await getReferenceSections(library, version)
|
||||
const { meta, content } = await getReferenceContent(library, version)
|
||||
|
||||
return (
|
||||
@@ -89,7 +89,7 @@ async function ReferencePage() {
|
||||
library={library}
|
||||
version={currentVersion}
|
||||
isLatestVersion={isLatestVersion}
|
||||
sections={[]}
|
||||
sections={sections}
|
||||
meta={meta}
|
||||
content={content}
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
||||
import { join, dirname, resolve } from 'path'
|
||||
import { mkdirSync, readdirSync, writeFileSync } from 'fs'
|
||||
import { join, dirname, relative } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { processSpec } from './process-tsdoc.js'
|
||||
import { processSpec, type SpecCategory, type SpecConfig } from './process-tsdoc.js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const CONFIG_PATH = join(__dirname, '../spec/enrichments/tsdoc_v2/config.json')
|
||||
const config: {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
referenceLink?: string
|
||||
referenceLinkLabel?: string
|
||||
} = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'))
|
||||
// ---------------------------------------------------------------------------
|
||||
// Folder discovery
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Recursively finds all folders under `baseDir` that directly contain at least
|
||||
* one .json file other than config.json. These are treated as spec source folders.
|
||||
*/
|
||||
function findSpecFolders(baseDir: string): string[] {
|
||||
const result: string[] = []
|
||||
function walk(dir: string) {
|
||||
const entries = readdirSync(dir, { withFileTypes: true })
|
||||
const hasSource = entries.some(
|
||||
(e) => e.isFile() && e.name.endsWith('.json') && e.name !== 'config.json'
|
||||
)
|
||||
if (hasSource) result.push(dir)
|
||||
for (const e of entries) {
|
||||
if (e.isDirectory()) walk(join(dir, e.name))
|
||||
}
|
||||
}
|
||||
walk(baseDir)
|
||||
return result
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function escapeMdxProse(text: string): string {
|
||||
// Escape { and } outside code blocks/spans so MDX doesn't treat them as JSX.
|
||||
@@ -25,6 +45,35 @@ function prop(value: unknown): string {
|
||||
return `{${JSON.stringify(value)}}`
|
||||
}
|
||||
|
||||
function toSlug(name: string): string {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sections builder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function buildSections(categories: SpecCategory[]): object[] {
|
||||
return categories.map(({ category, definitions }) => ({
|
||||
id: toSlug(category),
|
||||
type: 'category',
|
||||
title: category,
|
||||
items: definitions.map((def) => ({
|
||||
id: def.name,
|
||||
type: 'function',
|
||||
title: def.name,
|
||||
slug: def.name,
|
||||
})),
|
||||
}))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MDX builder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let exampleCounter = 0
|
||||
|
||||
function generateExamplesBlock(examples: any[]): string[] {
|
||||
@@ -86,14 +135,16 @@ function generateExamplesBlock(examples: any[]): string[] {
|
||||
return lines
|
||||
}
|
||||
|
||||
function generateMdx(categories: ReturnType<typeof processSpec>): string {
|
||||
function generateMdx(categories: SpecCategory[], config: SpecConfig): string {
|
||||
const fmVal = (v: string) => `"${v.replace(/"/g, '\\"')}"`
|
||||
const frontmatter = [
|
||||
'---',
|
||||
`title: ${fmVal(config.title ?? 'Reference')}`,
|
||||
...(config.subtitle ? [`subtitle: ${fmVal(config.subtitle)}`] : []),
|
||||
...(config.referenceLink ? [`referenceLink: ${fmVal(config.referenceLink)}`] : []),
|
||||
...(config.referenceLinkLabel ? [`referenceLinkLabel: ${fmVal(config.referenceLinkLabel)}`] : []),
|
||||
...(config.referenceLinkLabel
|
||||
? [`referenceLinkLabel: ${fmVal(config.referenceLinkLabel)}`]
|
||||
: []),
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -145,13 +196,43 @@ function generateMdx(categories: ReturnType<typeof processSpec>): string {
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
const processed = processSpec()
|
||||
const mdx = generateMdx(processed)
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main: discover and process all spec folders
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const outPath = join(__dirname, '../content/reference/javascript.mdx')
|
||||
mkdirSync(dirname(outPath), { recursive: true })
|
||||
writeFileSync(outPath, mdx)
|
||||
console.log(`MDX written to ${outPath}`)
|
||||
console.log(
|
||||
`${processed.length} categories, ${processed.flatMap((c) => c.definitions).length} definitions`
|
||||
)
|
||||
const specRefDir = join(__dirname, '../spec/reference')
|
||||
const contentRefDir = join(__dirname, '../content/reference')
|
||||
|
||||
const specFolders = findSpecFolders(specRefDir)
|
||||
|
||||
if (specFolders.length === 0) {
|
||||
console.warn(`No spec folders found under ${specRefDir}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
for (const specDir of specFolders) {
|
||||
exampleCounter = 0 // reset per spec so IDs don't bleed across files
|
||||
|
||||
const { categories, config } = processSpec(specDir)
|
||||
|
||||
// Mirror the folder path: spec/reference/<rel> → content/reference/<rel>
|
||||
const rel = relative(specRefDir, specDir)
|
||||
const outDir = join(contentRefDir, rel)
|
||||
mkdirSync(outDir, { recursive: true })
|
||||
|
||||
// Write MDX
|
||||
const mdx = generateMdx(categories, config)
|
||||
const mdxPath = join(outDir, 'index.mdx')
|
||||
writeFileSync(mdxPath, mdx)
|
||||
|
||||
// Write sections
|
||||
const sections = buildSections(categories)
|
||||
const sectionsPath = join(outDir, 'sections.json')
|
||||
writeFileSync(sectionsPath, JSON.stringify(sections, null, 2))
|
||||
|
||||
console.log(`[${rel}] MDX → ${mdxPath}`)
|
||||
console.log(`[${rel}] sections → ${sectionsPath}`)
|
||||
console.log(
|
||||
`[${rel}] ${categories.length} categories, ${categories.flatMap((c) => c.definitions).length} definitions`
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { join, dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
import { readFileSync, readdirSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
type ContentItem = { kind: string; text: string }
|
||||
type BlockTag = { tag: string; name?: string; content?: ContentItem[] }
|
||||
@@ -167,6 +164,7 @@ function parseExamples(blockTags: BlockTag[]): any[] {
|
||||
const responseTag = findByName(responses, title) ?? responses[i]
|
||||
const descTag = findByName(descs, title) ?? descs[i]
|
||||
|
||||
// Use findLast so inline code spans (e.g. `select()`) before the fenced block are skipped
|
||||
const sqlBlock = sqlTag?.content?.findLast((c) => c.kind === 'code')
|
||||
const responseBlock = responseTag?.content?.findLast((c) => c.kind === 'code')
|
||||
const notes = descTag?.content
|
||||
@@ -205,29 +203,39 @@ function collectDeclarations(node: any): any[] {
|
||||
return out
|
||||
}
|
||||
|
||||
const SOURCE_FILES = [
|
||||
'functions.json',
|
||||
'gotrue.json',
|
||||
'postgrest.json',
|
||||
'realtime.json',
|
||||
'storage.json',
|
||||
'supabase.json',
|
||||
]
|
||||
export interface SpecConfig {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
referenceLink?: string
|
||||
referenceLinkLabel?: string
|
||||
ignoreDefinitions?: string[]
|
||||
categoryOrder?: string[]
|
||||
}
|
||||
|
||||
export function processSpec() {
|
||||
const specDir = join(__dirname, '../spec/enrichments/tsdoc_v2')
|
||||
export interface SpecCategory {
|
||||
category: string
|
||||
definitions: any[]
|
||||
}
|
||||
|
||||
const config: { ignoreDefinitions?: string[]; categoryOrder?: string[] } = (() => {
|
||||
export function processSpec(specDir: string): { categories: SpecCategory[]; config: SpecConfig } {
|
||||
// Load config (optional — missing config is fine)
|
||||
const config: SpecConfig = (() => {
|
||||
try {
|
||||
return JSON.parse(readFileSync(join(specDir, 'config.json'), 'utf-8'))
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
})()
|
||||
|
||||
const ignoredNames = new Set(config.ignoreDefinitions ?? [])
|
||||
const categoryOrder = config.categoryOrder ?? []
|
||||
|
||||
const roots = SOURCE_FILES.map((f) => JSON.parse(readFileSync(join(specDir, f), 'utf-8')))
|
||||
// Discover all JSON source files in the spec folder, excluding config.json
|
||||
const sourceFiles = readdirSync(specDir)
|
||||
.filter((f) => f.endsWith('.json') && f !== 'config.json')
|
||||
.map((f) => join(specDir, f))
|
||||
|
||||
const roots = sourceFiles.map((f) => JSON.parse(readFileSync(f, 'utf-8')))
|
||||
|
||||
// Build a per-file targetMap so IDs from different packages don't collide
|
||||
const fileMaps = roots.map((root) => buildTargetMap(root))
|
||||
@@ -244,14 +252,14 @@ export function processSpec() {
|
||||
if (!categoryTag) continue // skip internal declarations with no category
|
||||
const category = (categoryTag.content?.[0]?.text ?? '').split('\n')[0].trim()
|
||||
|
||||
if (ignoredNames.has(decl.name)) continue
|
||||
|
||||
if (!categoryMap.has(category)) categoryMap.set(category, [])
|
||||
|
||||
const sig = decl.signatures[0]
|
||||
const remarkTags = blockTags.filter((t) => t.tag === '@remarks')
|
||||
const examples = parseExamples(blockTags)
|
||||
|
||||
if (ignoredNames.has(decl.name)) continue
|
||||
|
||||
const definition: any = {
|
||||
name: decl.name,
|
||||
description: contentToMd(decl.comment?.summary ?? []),
|
||||
@@ -266,7 +274,7 @@ export function processSpec() {
|
||||
categoryMap.get(category)!.push(definition)
|
||||
}
|
||||
|
||||
const result = Array.from(categoryMap.entries())
|
||||
const categories = Array.from(categoryMap.entries())
|
||||
.map(([category, definitions]) => ({ category, definitions }))
|
||||
.sort((a, b) => {
|
||||
const ai = categoryOrder.indexOf(a.category)
|
||||
@@ -281,20 +289,5 @@ export function processSpec() {
|
||||
return 0
|
||||
})
|
||||
|
||||
console.log(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Only write to disk when run directly
|
||||
const isMain = fileURLToPath(import.meta.url) === resolve(process.argv[1])
|
||||
if (isMain) {
|
||||
const output = processSpec()
|
||||
writeFileSync(
|
||||
join(__dirname, '../spec/enrichments/tsdoc_v2/processed.json'),
|
||||
JSON.stringify(output, null, 2)
|
||||
)
|
||||
console.log(
|
||||
`Done: ${output.length} categories, ${output.flatMap((c) => c.definitions).length} declarations`
|
||||
)
|
||||
return { categories, config }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"title":"JavaScript Client library",
|
||||
"subtitle": "Use the `supabase-js` package to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.",
|
||||
"referenceLink": "https://github.com/supabase/supabase-js",
|
||||
"referenceLinkLabel": "@supabase/supabase-js",
|
||||
"versions": [
|
||||
"v1",
|
||||
"v2"
|
||||
],
|
||||
"isLatestVersion": true,
|
||||
"version": "v2",
|
||||
"ignoreDefinitions": [
|
||||
"constructor"
|
||||
],
|
||||
"categoryOrder": [ "Initializing", "Database", "Auth", "Functions", "Realtime", "Storage" ]
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user