105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
export interface SchemaField {
|
|
name: string
|
|
type: string
|
|
isId: boolean
|
|
isUnique: boolean
|
|
isOptional: boolean
|
|
isList: boolean
|
|
isRelation: boolean
|
|
default?: string
|
|
}
|
|
|
|
export interface SchemaRelation {
|
|
from: string
|
|
fromField: string
|
|
to: string
|
|
toField: string
|
|
onDelete?: string
|
|
}
|
|
|
|
export interface SchemaModel {
|
|
name: string
|
|
tableName: string
|
|
fields: SchemaField[]
|
|
}
|
|
|
|
export interface SchemaEnum {
|
|
name: string
|
|
values: string[]
|
|
}
|
|
|
|
export interface ParsedSchema {
|
|
models: SchemaModel[]
|
|
enums: SchemaEnum[]
|
|
relations: SchemaRelation[]
|
|
}
|
|
|
|
export function parseSchema(raw: string): ParsedSchema {
|
|
const models: SchemaModel[] = []
|
|
const enums: SchemaEnum[] = []
|
|
const relations: SchemaRelation[] = []
|
|
|
|
const blocks = raw.match(/(model|enum)\s+(\w+)\s*\{([^}]*)}/gs) ?? []
|
|
|
|
for (const block of blocks) {
|
|
const match = block.match(/(model|enum)\s+(\w+)\s*\{([^}]*)}/s)
|
|
if (!match) continue
|
|
const [, type, name, body] = match
|
|
const lines = body
|
|
.split('\n')
|
|
.map((l) => l.trim())
|
|
.filter((l) => l && !l.startsWith('//'))
|
|
|
|
if (type === 'enum') {
|
|
enums.push({ name, values: lines })
|
|
continue
|
|
}
|
|
|
|
let tableName = name
|
|
const fields: SchemaField[] = []
|
|
|
|
for (const line of lines) {
|
|
const mapMatch = line.match(/@@map\("(\w+)"\)/)
|
|
if (mapMatch) { tableName = mapMatch[1]; continue }
|
|
if (line.startsWith('@@')) continue
|
|
|
|
const fieldMatch = line.match(/^(\w+)\s+(\w+)(\?)?(\[\])?\s*(.*)$/)
|
|
if (!fieldMatch) continue
|
|
const [, fName, fType, optional, list, attrs] = fieldMatch
|
|
|
|
const isId = attrs.includes('@id')
|
|
const isUnique = attrs.includes('@unique')
|
|
const isRelation = attrs.includes('@relation')
|
|
const defaultMatch = attrs.match(/@default\(([^)]+)\)/)
|
|
|
|
const isModelRef =
|
|
/^[A-Z]/.test(fType) &&
|
|
!enums.some((e) => e.name === fType) &&
|
|
!['String', 'Int', 'Float', 'Boolean', 'DateTime', 'BigInt', 'Decimal', 'Bytes', 'Json'].includes(fType)
|
|
|
|
if (isRelation) {
|
|
const relMatch = attrs.match(
|
|
/@relation\(fields:\s*\[(\w+)],\s*references:\s*\[(\w+)](?:,\s*onDelete:\s*(\w+))?\)/,
|
|
)
|
|
if (relMatch) {
|
|
relations.push({ from: name, fromField: relMatch[1], to: fType, toField: relMatch[2], onDelete: relMatch[3] })
|
|
}
|
|
}
|
|
|
|
fields.push({
|
|
name: fName,
|
|
type: fType + (list ? '[]' : ''),
|
|
isId, isUnique,
|
|
isOptional: !!optional,
|
|
isList: !!list,
|
|
isRelation: isModelRef,
|
|
default: defaultMatch?.[1],
|
|
})
|
|
}
|
|
|
|
models.push({ name, tableName, fields })
|
|
}
|
|
|
|
return { models, enums, relations }
|
|
}
|