@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
124 lines (123 loc) • 4.16 kB
JavaScript
/*
* @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);
}
}