UNPKG

@adonisjs/lucid

Version:

SQL ORM built on top of Active Record pattern

124 lines (123 loc) 4.16 kB
/* * @adonisjs/lucid * * (c) Harminder Virk <virk@adonisjs.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { dirname } from 'node:path'; import { EventEmitter } from 'node:events'; import { writeFile, mkdir } from 'node:fs/promises'; import { RuntimeException } from '@adonisjs/core/exceptions'; import { OrmSchemaBuilder } from "./builder.js"; /** * OrmSchemaGenerator orchestrates the process of generating TypeScript * model schemas from database tables. */ export class OrmSchemaGenerator extends EventEmitter { db; application; config; /** * Schema builder instance */ builder; /** * Query client instance */ connection; constructor(db, application, config) { super(); this.db = db; this.application = application; this.config = config; const connectionName = this.config.connectionName || this.db.primaryConnectionName; this.connection = this.db.connection(connectionName); this.builder = new OrmSchemaBuilder(this.connection); } /** * Load schema rules from the configured paths */ async loadSchemaRules() { if (!this.config.rulesPaths || this.config.rulesPaths.length === 0) { return; } try { const rules = await Promise.all(this.config.rulesPaths.map((rulesPath) => { return this.application.importDefault(rulesPath); })); this.builder.loadRules(rules); } catch (error) { throw new RuntimeException('Failed to load schema rules', { cause: error, }); } } /** * Fetch all tables and their columns from the database */ async fetchTablesAndColumns() { /** * Get list of all tables from the database */ let tables = await this.connection.getAllTablesWithSchema(this.config.schemas); if (this.config.excludeTables) { tables = tables.filter(({ name, schema }) => !this.config.excludeTables?.includes(name) && !(schema && this.config.excludeTables?.includes(`${schema}.${name}`))); } this.emit('collect:tables', tables.map(({ name }) => name)); /** * Fetch columns and primary keys for each table */ const tablesWithColumns = await Promise.all(tables.map(async ({ name, schema }) => { const tableLookup = schema ? `${schema}.${name}` : name; const [columns, primaryKeys] = await Promise.all([ this.connection.columnsInfo(name, undefined, schema), this.connection.getPrimaryKeys(tableLookup), ]); this.emit('table:info', { tableName: name, columns }); return { name, columns, primaryKeys }; })); return tablesWithColumns; } /** * Generate schemas and write to output file */ async generate() { /** * Load custom schema rules if provided */ await this.loadSchemaRules(); /** * Fetch all tables and columns */ const tables = await this.fetchTablesAndColumns(); /** * Generate schemas using the builder */ this.emit('generating:schema'); const schemas = this.builder.generateSchemas(tables); const output = this.builder.getOutput(schemas); /** * Write to output file */ const outputDir = dirname(this.config.outputPath); await mkdir(outputDir, { recursive: true }); const comment = [ '/**', ' * This file is automatically generated', ' * DO NOT EDIT manually', ' * Run "node ace migration:run" command to re-generate this file', ' */', ].join('\n'); await writeFile(this.config.outputPath, `${comment}\n\n${output}`, 'utf-8'); } /** * Close database connections */ async close() { await this.db.manager.closeAll(true); } }