Add initial project files
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import { getColumnsForTable, getForeignKeysReferencing } from '#services/database_inspector'
|
||||
import {
|
||||
mapDBTypeToFrontend,
|
||||
mapDBTypeToTs,
|
||||
mapDBTypeToValidator,
|
||||
} from '#services/module_generator/db_types'
|
||||
import { camelCase, capitalize } from '#services/module_generator/naming'
|
||||
|
||||
export interface EnhancedColumn {
|
||||
name: string
|
||||
camelCase: string
|
||||
type: string
|
||||
isPrimary: boolean
|
||||
isPassword: boolean
|
||||
isEmail: boolean
|
||||
isDateTime: boolean
|
||||
isSpecial: boolean
|
||||
isRequired: boolean
|
||||
isNullable: boolean
|
||||
validatorType: string
|
||||
needsTrim: boolean
|
||||
}
|
||||
|
||||
export interface TestField {
|
||||
camelCase: string
|
||||
sampleValue: string | number | boolean
|
||||
column_name: string
|
||||
}
|
||||
|
||||
export interface ModuleContext {
|
||||
hasPassword: boolean
|
||||
enhancedColumns: EnhancedColumn[]
|
||||
createFields: TestField[]
|
||||
updateFields: TestField[]
|
||||
insertJson: string
|
||||
frontendFields: Array<{ name: string; type: string }>
|
||||
relationships: { imports: string[]; code: string[] }
|
||||
}
|
||||
|
||||
/** Derives everything the templates and builders need from the table schema. */
|
||||
export async function buildModuleContext(table: string): Promise<ModuleContext> {
|
||||
const columns = await getColumnsForTable(table)
|
||||
|
||||
const enhancedColumns = columns.map((col): EnhancedColumn => {
|
||||
const colName = col.column_name
|
||||
const type = mapDBTypeToTs(col.data_type) + (col.is_nullable === 'YES' ? ' | null' : '')
|
||||
return {
|
||||
name: colName,
|
||||
camelCase: camelCase(colName),
|
||||
type,
|
||||
isPrimary: colName === 'id',
|
||||
isPassword: colName === 'password',
|
||||
isEmail: colName === 'email',
|
||||
isDateTime: ['created_at', 'updated_at'].includes(colName),
|
||||
isSpecial: ['created_at', 'updated_at', 'deleted_at'].includes(colName),
|
||||
isRequired: col.is_nullable === 'NO',
|
||||
isNullable: col.is_nullable === 'YES',
|
||||
validatorType: mapDBTypeToValidator(col.data_type),
|
||||
needsTrim:
|
||||
mapDBTypeToValidator(col.data_type) === 'string' &&
|
||||
colName !== 'email' &&
|
||||
colName !== 'password',
|
||||
}
|
||||
})
|
||||
|
||||
const sampleFor = (col: EnhancedColumn) => {
|
||||
const t = col.validatorType
|
||||
if (t === 'number') return 1
|
||||
if (t === 'boolean') return false
|
||||
if (t === 'date') return `'${new Date().toISOString()}'`
|
||||
return `'sample_${col.camelCase}'`
|
||||
}
|
||||
|
||||
const toTestField = (col: EnhancedColumn): TestField => ({
|
||||
camelCase: col.camelCase,
|
||||
sampleValue: sampleFor(col),
|
||||
column_name: col.name,
|
||||
})
|
||||
|
||||
const createFields = enhancedColumns.filter((c) => !c.isPrimary && !c.isSpecial).map(toTestField)
|
||||
const updateFields = enhancedColumns.filter((c) => !c.isPrimary && !c.isDateTime).map(toTestField)
|
||||
|
||||
// minimal insert json for show/update/destroy fixtures
|
||||
const insertJson = `{
|
||||
${createFields.map((f) => ` ${f.column_name}: ${f.sampleValue}`).join(',\n')}
|
||||
}`
|
||||
|
||||
const frontendFields = columns.map((col) => ({
|
||||
name: col.column_name,
|
||||
type: mapDBTypeToFrontend(col.data_type) + (col.is_nullable === 'YES' ? ' | null' : ''),
|
||||
}))
|
||||
|
||||
return {
|
||||
hasPassword: columns.some((col) => col.column_name === 'password'),
|
||||
enhancedColumns,
|
||||
createFields,
|
||||
updateFields,
|
||||
insertJson,
|
||||
frontendFields,
|
||||
relationships: await buildRelationships(table),
|
||||
}
|
||||
}
|
||||
|
||||
/** Lucid relationship decorators inferred from the `<table>_id` column convention. */
|
||||
async function buildRelationships(table: string) {
|
||||
const lines: string[] = []
|
||||
const imports: Set<string> = new Set()
|
||||
|
||||
const columns = await getColumnsForTable(table)
|
||||
const references = await getForeignKeysReferencing(table)
|
||||
|
||||
for (const col of columns) {
|
||||
if (col.column_name.endsWith('_id')) {
|
||||
const refTable = col.column_name.replace('_id', '')
|
||||
const refModel = capitalize(refTable)
|
||||
imports.add('belongsTo')
|
||||
lines.push(
|
||||
` @belongsTo(() => ${refModel})\n declare ${refTable}: BelongsTo<typeof ${refModel}>`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (const ref of references) {
|
||||
const relModel = capitalize(ref.table_name)
|
||||
imports.add('hasMany')
|
||||
lines.push(
|
||||
` @hasMany(() => ${relModel})\n declare ${ref.table_name}: HasMany<typeof ${relModel}>`
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
imports: Array.from(imports),
|
||||
code: lines,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user