typeorm-extension
Version:
A library to create/drop database, simple seeding data sets, ...
274 lines (268 loc) • 10.5 kB
JavaScript
#!/usr/bin/env node
import 'reflect-metadata';
import process$1 from 'node:process';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { consola } from 'consola';
import { resolveFilePath, readTSConfig, adjustFilePath, parseFilePath, buildDataSourceOptions, createDatabase, dropDatabase, isDirectory, buildSeederFileTemplate, setDataSourceOptions, useDataSource, SeederExecutor } from 'typeorm-extension';
import { getFileNameExtension, removeFileNameExtension } from 'locter';
import fs from 'node:fs';
import path from 'node:path';
import { pascalCase } from 'pascal-case';
class DatabaseCreateCommand {
builder(args) {
return args.option('preserveFilePaths', {
default: false,
type: 'boolean',
describe: 'This option indicates if file paths should be preserved.'
}).option('root', {
alias: 'r',
default: process.cwd(),
describe: 'Root directory of the project.'
}).option('tsconfig', {
alias: 'tc',
default: 'tsconfig.json',
describe: 'Name (or relative path incl. name) of the tsconfig file.'
}).option('dataSource', {
alias: 'd',
default: 'data-source',
describe: 'Name (or relative path incl. name) of the data-source file.'
}).option('synchronize', {
alias: 's',
default: 'yes',
describe: 'Create database schema for all entities.',
choices: [
'yes',
'no'
]
}).option('initialDatabase', {
describe: 'Specify the initial database to connect to.'
});
}
async handler(raw) {
const args = raw;
let tsconfig;
let sourcePath = resolveFilePath(args.dataSource, args.root);
if (!args.preserveFilePaths) {
tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
sourcePath = await adjustFilePath(sourcePath, tsconfig);
}
const source = parseFilePath(sourcePath);
consola.info(`DataSource Directory: ${source.directory}`);
consola.info(`DataSource Name: ${source.name}`);
const dataSourceOptions = await buildDataSourceOptions({
directory: source.directory,
dataSourceName: source.name,
tsconfig,
preserveFilePaths: args.preserveFilePaths
});
const context = {
ifNotExist: true,
options: dataSourceOptions
};
if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
context.initialDatabase = args.initialDatabase;
}
context.synchronize = args.synchronize === 'yes';
try {
await createDatabase(context);
consola.success('Created database.');
process.exit(0);
} catch (e) {
consola.warn('Failed to create database.');
consola.error(e);
process.exit(1);
}
}
constructor(){
this.command = 'db:create';
this.describe = 'Create database.';
}
}
class DatabaseDropCommand {
builder(args) {
return args.option('preserveFilePaths', {
default: false,
type: 'boolean',
describe: 'This option indicates if file paths should be preserved.'
}).option('root', {
alias: 'r',
default: process.cwd(),
describe: 'Root directory of the project.'
}).option('tsconfig', {
alias: 'tc',
default: 'tsconfig.json',
describe: 'Name (or relative path incl. name) of the tsconfig file.'
}).option('dataSource', {
alias: 'd',
default: 'data-source',
describe: 'Name (or relative path incl. name) of the data-source file.'
}).option('initialDatabase', {
describe: 'Specify the initial database to connect to.'
});
}
async handler(raw) {
const args = raw;
let tsconfig;
let sourcePath = resolveFilePath(args.dataSource, args.root);
if (!args.preserveFilePaths) {
tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
sourcePath = await adjustFilePath(sourcePath, tsconfig);
}
const source = parseFilePath(sourcePath);
consola.info(`DataSource Directory: ${source.directory}`);
consola.info(`DataSource Name: ${source.name}`);
const dataSourceOptions = await buildDataSourceOptions({
directory: source.directory,
dataSourceName: source.name,
tsconfig,
preserveFilePaths: args.preserveFilePaths
});
const context = {
ifExist: true,
options: dataSourceOptions
};
if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
context.initialDatabase = args.initialDatabase;
}
try {
await dropDatabase(context);
consola.success('Dropped database.');
process.exit(0);
} catch (e) {
consola.warn('Failed to drop database.');
consola.error(e);
process.exit(1);
}
}
constructor(){
this.command = 'db:drop';
this.describe = 'Drop database.';
}
}
class SeedCreateCommand {
builder(args) {
return args.option('root', {
alias: 'r',
default: process.cwd(),
describe: 'Root directory of the project.'
}).option('timestamp', {
alias: 't',
type: 'number',
describe: 'Custom timestamp for the seeder name.'
}).option('javascript', {
alias: 'j',
type: 'boolean',
default: false,
describe: 'Generate a seeder file for JavaScript instead of TypeScript.'
}).option('name', {
alias: 'n',
describe: 'Name (or relative path incl. name) of the seeder.',
demandOption: true
});
}
async handler(raw) {
const args = raw;
let timestamp;
if (Number.isNaN(args.timestamp) || !args.timestamp) {
timestamp = Date.now();
} else {
timestamp = args.timestamp;
}
const sourcePath = parseFilePath(args.name, args.root);
const dirNameIsDirectory = await isDirectory(sourcePath.directory);
if (!dirNameIsDirectory) {
consola.warn(`The output directory ${sourcePath.directory} does not exist.`);
process.exit(1);
}
const extension = args.javascript ? '.js' : '.ts';
const nameExtension = getFileNameExtension(sourcePath.name);
const nameWithoutExtension = removeFileNameExtension(sourcePath.name);
let fileName;
if (nameExtension) {
fileName = `${timestamp}-${sourcePath.name}`;
} else {
fileName = `${timestamp}-${sourcePath.name}${extension}`;
}
const filePath = sourcePath.directory + path.sep + fileName;
const template = buildSeederFileTemplate(nameWithoutExtension, timestamp);
consola.info(`Seed Directory: ${sourcePath.directory}`);
consola.info(`Seed FileName: ${fileName}`);
consola.info(`Seed Name: ${pascalCase(nameWithoutExtension)}`);
try {
await fs.promises.writeFile(filePath, template, {
encoding: 'utf-8'
});
} catch (e) {
consola.warn(`The seed could not be written to the path ${filePath}.`);
process.exit(1);
}
process.exit(0);
}
constructor(){
this.command = 'seed:create';
this.describe = 'Create a seeder file.';
}
}
class SeedRunCommand {
builder(args) {
return args.option('preserveFilePaths', {
default: false,
type: 'boolean',
describe: 'This option indicates if file paths should be preserved.'
}).option('root', {
alias: 'r',
default: process.cwd(),
describe: 'Root directory of the project.'
}).option('tsconfig', {
alias: 'tc',
default: 'tsconfig.json',
describe: 'Name (or relative path incl. name) of the tsconfig file.'
}).option('dataSource', {
alias: 'd',
default: 'data-source',
describe: 'Name (or relative path incl. name) of the data-source file.'
}).option('name', {
alias: 'n',
describe: 'Name (or relative path incl. name) of the seeder.'
});
}
async handler(raw) {
const args = raw;
let tsconfig;
let sourcePath = resolveFilePath(args.dataSource, args.root);
if (!args.preserveFilePaths) {
tsconfig = await readTSConfig(args.root);
sourcePath = await adjustFilePath(sourcePath, tsconfig);
args.name = await adjustFilePath(args.name, tsconfig);
}
const source = parseFilePath(sourcePath);
consola.info(`DataSource Directory: ${source.directory}`);
consola.info(`DataSource Name: ${source.name}`);
const dataSourceOptions = await buildDataSourceOptions({
dataSourceName: source.name,
directory: source.directory,
tsconfig,
preserveFilePaths: args.preserveFilePaths
});
setDataSourceOptions(dataSourceOptions);
if (args.name) {
consola.info(`Seed Name: ${args.name}`);
}
const dataSource = await useDataSource();
const executor = new SeederExecutor(dataSource, {
root: args.root,
tsconfig,
preserveFilePaths: args.preserveFilePaths
});
await executor.execute({
seedName: args.name
});
process.exit(0);
}
constructor(){
this.command = 'seed:run';
this.describe = 'Populate the database with an initial data set or generated data by a factory.';
}
}
yargs(hideBin(process$1.argv)).scriptName('typeorm-extension').usage('Usage: $0 <command> [options]').demandCommand(1).command(new DatabaseCreateCommand()).command(new DatabaseDropCommand()).command(new SeedRunCommand()).command(new SeedCreateCommand()).strict().alias('v', 'version').help('h').alias('h', 'help').parse();