mirror of
https://github.com/supabase/supabase.git
synced 2026-07-06 03:04:20 +08:00
Scripts currently use CJS, which is causing a bit of a mess when trying to use shared utilities from the app. Converting everything to ESM so there are fewer conflicts when adding new scripts going forward.
277 lines
7.6 KiB
TypeScript
277 lines
7.6 KiB
TypeScript
import { createHash } from 'crypto'
|
|
import { readFile } from 'fs/promises'
|
|
import yaml from 'js-yaml'
|
|
import type { OpenAPIV3 } from 'openapi-types'
|
|
import type {
|
|
ICommonItem,
|
|
ICommonSection,
|
|
IFunctionDefinition,
|
|
ISpec,
|
|
} from '../../../components/reference/Reference.types.js'
|
|
import type { CliCommand, CliSpec } from '../../../generator/types/CliSpec.js'
|
|
import { flattenSections } from '../../../lib/helpers.js'
|
|
import { enrichedOperation, gen_v3 } from '../../../lib/refGenerator/helpers.js'
|
|
import type { Json } from '../../helpers.mdx.js'
|
|
import { BaseLoader, BaseSource } from './base.js'
|
|
|
|
export abstract class ReferenceLoader<SpecSection> extends BaseLoader {
|
|
type = 'reference' as const
|
|
sourceConstructor: (
|
|
...args: ConstructorParameters<typeof ReferenceSource<SpecSection>>
|
|
) => ReferenceSource<SpecSection>
|
|
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
public meta: Json,
|
|
public specFilePath: string,
|
|
public sectionsFilePath: string
|
|
) {
|
|
super(source, path)
|
|
}
|
|
|
|
async load() {
|
|
const specContents = await readFile(this.specFilePath, 'utf8')
|
|
const refSectionsContents = await readFile(this.sectionsFilePath, 'utf8')
|
|
|
|
const refSections: ICommonItem[] = JSON.parse(refSectionsContents)
|
|
const flattenedRefSections = flattenSections(refSections)
|
|
|
|
const specSections = this.getSpecSections(specContents)
|
|
|
|
const sections = flattenedRefSections
|
|
.map((refSection) => {
|
|
const specSection = this.matchSpecSection(specSections, refSection.id)
|
|
|
|
if (!specSection) {
|
|
return
|
|
}
|
|
|
|
return this.sourceConstructor(
|
|
this.source,
|
|
`${this.path}/${refSection.slug}`,
|
|
refSection,
|
|
specSection,
|
|
this.enhanceMeta(specSection)
|
|
)
|
|
})
|
|
.filter(Boolean)
|
|
|
|
return sections
|
|
}
|
|
|
|
abstract getSpecSections(specContents: string): SpecSection[]
|
|
abstract matchSpecSection(specSections: SpecSection[], id: string): SpecSection
|
|
enhanceMeta(section: SpecSection): Json {
|
|
return this.meta
|
|
}
|
|
}
|
|
|
|
export abstract class ReferenceSource<SpecSection> extends BaseSource {
|
|
type = 'reference' as const
|
|
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
public refSection: ICommonSection,
|
|
public specSection: SpecSection,
|
|
public meta: Json
|
|
) {
|
|
super(source, path)
|
|
}
|
|
|
|
process() {
|
|
const checksum = createHash('sha256')
|
|
.update(JSON.stringify(this.refSection) + JSON.stringify(this.specSection))
|
|
.digest('base64')
|
|
|
|
const sections = [
|
|
{
|
|
heading: this.refSection.title,
|
|
slug: this.refSection.slug,
|
|
content: `${this.meta.title} for ${this.refSection.title}:\n${this.formatSection(
|
|
this.specSection,
|
|
this.refSection
|
|
)}`,
|
|
},
|
|
]
|
|
|
|
this.checksum = checksum
|
|
this.sections = sections
|
|
|
|
return {
|
|
checksum,
|
|
sections,
|
|
meta: {
|
|
...this.meta,
|
|
subtitle: this.extractSubtitle(),
|
|
title: this.extractTitle(),
|
|
},
|
|
}
|
|
}
|
|
|
|
abstract formatSection(specSection: SpecSection, refSection: ICommonItem): string
|
|
abstract extractTitle(): string
|
|
abstract extractSubtitle(): string
|
|
}
|
|
|
|
export class OpenApiReferenceLoader extends ReferenceLoader<enrichedOperation> {
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
meta: Json,
|
|
specFilePath: string,
|
|
sectionsFilePath: string
|
|
) {
|
|
super(source, path, meta, specFilePath, sectionsFilePath)
|
|
this.sourceConstructor = (...args) => new OpenApiReferenceSource(...args)
|
|
}
|
|
|
|
getSpecSections(specContents: string): enrichedOperation[] {
|
|
const spec: OpenAPIV3.Document<{}> = JSON.parse(specContents)
|
|
|
|
const generatedSpec = gen_v3(spec, '', {
|
|
apiUrl: 'apiv0',
|
|
})
|
|
|
|
return generatedSpec.operations
|
|
}
|
|
matchSpecSection(operations: enrichedOperation[], id: string): enrichedOperation {
|
|
return operations.find((operation) => operation.operationId === id)
|
|
}
|
|
}
|
|
|
|
export class OpenApiReferenceSource extends ReferenceSource<enrichedOperation> {
|
|
formatSection(specOperation: enrichedOperation, _: ICommonItem) {
|
|
const { summary, description, operation, path, tags } = specOperation
|
|
return JSON.stringify({
|
|
summary,
|
|
description,
|
|
operation,
|
|
path,
|
|
tags,
|
|
})
|
|
}
|
|
|
|
extractSubtitle() {
|
|
return `${this.meta.title}: ${this.specSection.description}`
|
|
}
|
|
|
|
extractTitle() {
|
|
return (
|
|
this.specSection.summary ||
|
|
(typeof this.meta.title === 'string' ? this.meta.title : this.specSection.operation)
|
|
)
|
|
}
|
|
|
|
extractIndexedContent(): string {
|
|
const { summary, description, operation, tags } = this.specSection
|
|
return `# ${this.meta.title ?? ''}\n\n${summary ?? ''}\n\n${description ?? ''}\n\n${operation ?? ''}\n\n${tags.join(
|
|
', '
|
|
)}`
|
|
}
|
|
}
|
|
|
|
export class ClientLibReferenceLoader extends ReferenceLoader<IFunctionDefinition> {
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
meta: Json,
|
|
specFilePath: string,
|
|
sectionsFilePath: string
|
|
) {
|
|
super(source, path, meta, specFilePath, sectionsFilePath)
|
|
this.sourceConstructor = (...args) => new ClientLibReferenceSource(...args)
|
|
}
|
|
|
|
getSpecSections(specContents: string): IFunctionDefinition[] {
|
|
const spec = yaml.load(specContents) as ISpec
|
|
|
|
return spec.functions
|
|
}
|
|
|
|
matchSpecSection(functionDefinitions: IFunctionDefinition[], id: string): IFunctionDefinition {
|
|
return functionDefinitions.find((functionDefinition) => functionDefinition.id === id)
|
|
}
|
|
|
|
enhanceMeta(section: IFunctionDefinition): Json {
|
|
return { ...this.meta, slug: section.id, methodName: section.title }
|
|
}
|
|
}
|
|
|
|
export class ClientLibReferenceSource extends ReferenceSource<IFunctionDefinition> {
|
|
formatSection(functionDefinition: IFunctionDefinition, refSection: ICommonItem): string {
|
|
const { title } = refSection
|
|
const { description, title: functionName } = functionDefinition
|
|
|
|
return JSON.stringify({
|
|
title,
|
|
description,
|
|
functionName,
|
|
})
|
|
}
|
|
|
|
extractTitle(): string {
|
|
return this.specSection.title
|
|
}
|
|
|
|
extractSubtitle(): string {
|
|
return `${this.meta.title}: ${this.refSection.title}`
|
|
}
|
|
|
|
extractIndexedContent(): string {
|
|
const { title, description, examples } = this.specSection
|
|
const exampleText =
|
|
examples
|
|
?.map((example) => `### ${example.name ?? ''}\n\n${example.code ?? ''}`)
|
|
.join('\n\n') ?? ''
|
|
return `# ${this.meta.title ?? ''}\n\n${title ?? ''}\n\n${description ?? ''}\n\n## Examples\n\n${exampleText}`
|
|
}
|
|
}
|
|
|
|
export class CliReferenceLoader extends ReferenceLoader<CliCommand> {
|
|
constructor(
|
|
source: string,
|
|
path: string,
|
|
meta: Json,
|
|
specFilePath: string,
|
|
sectionsFilePath: string
|
|
) {
|
|
super(source, path, meta, specFilePath, sectionsFilePath)
|
|
this.sourceConstructor = (...args) => new CliReferenceSource(...args)
|
|
}
|
|
|
|
getSpecSections(specContents: string): CliCommand[] {
|
|
const spec = yaml.load(specContents) as CliSpec
|
|
|
|
return spec.commands
|
|
}
|
|
matchSpecSection(cliCommands: CliCommand[], id: string): CliCommand {
|
|
return cliCommands.find((cliCommand) => cliCommand.id === id)
|
|
}
|
|
}
|
|
|
|
export class CliReferenceSource extends ReferenceSource<CliCommand> {
|
|
formatSection(cliCommand: CliCommand, _: ICommonItem): string {
|
|
const { summary, description, usage } = cliCommand
|
|
return JSON.stringify({
|
|
summary,
|
|
description,
|
|
usage,
|
|
})
|
|
}
|
|
|
|
extractSubtitle(): string {
|
|
return `${this.meta.title}: ${this.specSection.title}`
|
|
}
|
|
|
|
extractTitle(): string {
|
|
return this.specSection.summary
|
|
}
|
|
|
|
extractIndexedContent(): string {
|
|
const { summary, description, usage } = this.specSection
|
|
return `# ${this.meta.title ?? ''}\n\n${summary ?? ''}\n\n${description ?? ''}\n\n${usage ?? ''}`
|
|
}
|
|
}
|