/* eslint-disable no-console */ import { existsSync, promises as fsp } from 'fs'; import { join, resolve } from 'path'; import { CommandModule } from 'yargs'; import { getTimestamp, green, red } from './utils'; /** * CLI command options object. */ interface GenerateMigrationOptions { includeMultitenancyFunctions: boolean; currentMigrationFolderPath: string; } const ensureEmptyCurrentSql = async ( currentMigrationFolderPath: string, ): Promise => { const dirPath = join(process.cwd(), currentMigrationFolderPath); const filePath = join(dirPath, 'current.sql'); if (existsSync(filePath)) { const currentSql = await fsp.readFile(filePath, 'utf8'); const currentSqlWithoutComments = currentSql.replace( /(--.*)|(((\/\*)+?[\w\W]+?(\*\/)+))/gm, '', ); const nonEmptyCurrentSqlLines = currentSqlWithoutComments.match(/^[^\S\r\n]*\S.*$/gm); if ( nonEmptyCurrentSqlLines !== null && nonEmptyCurrentSqlLines.length > 0 ) { console.error( getTimestamp(), red( 'Please ensure that your current.sql file is empty/only contains comments.', ), ); process.exit(-1); } } else if (!existsSync(dirPath)) { // File itself is created after contents are generated await fsp.mkdir(dirPath, { recursive: true }); } return filePath; }; const generateContent = async ( includeMultitenancyFunctions: boolean, ): Promise => { const defineFunctionsPath = resolve(__dirname, '../../../migrations/define'); const defineFunctions = await fsp.readFile( join(defineFunctionsPath, 'define-functions.sql'), ); let result = `--! Message: upgrade SQL define functions\n\n${defineFunctions}`; if (includeMultitenancyFunctions) { const multitenancyDefineFunctions = await fsp.readFile( join(defineFunctionsPath, 'define-multitenancy-functions.sql'), ); result = `${result}\n${multitenancyDefineFunctions}`; } return result; }; /** * Reads the latest contents of the SQL functions to easily define tables, * indexes, etc... We check that migration file is empty/only contains SQL * comments before updating it. If it did not exist - will create it. */ const generate = async ({ includeMultitenancyFunctions, currentMigrationFolderPath, // migrations }: GenerateMigrationOptions): Promise => { console.log( getTimestamp(), green('Starting define functions update migration generation!'), ); const absolutePath = await ensureEmptyCurrentSql(currentMigrationFolderPath); const migrationContent = await generateContent(includeMultitenancyFunctions); await fsp.writeFile(absolutePath, migrationContent); console.log( getTimestamp(), green(`Migration file ${currentMigrationFolderPath} updated!`), ); }; /** * Yargs command module definition for `generate-define-func-migration` CLI script. */ export const generateDefineFuncMigration: CommandModule< unknown, GenerateMigrationOptions > = { command: 'generate-define-func-migration', describe: 'Updates a current.sql migrations file with latest ax_define helper function definitions.', builder: (yargs) => yargs .option('includeMultitenancyFunctions', { alias: 'm', describe: 'If set to true, would also include multitenancy define functions.', default: false, boolean: true, hidden: true, }) .option('currentMigrationFolderPath', { alias: 'p', default: 'migrations', describe: 'Relative path from working directory to a folder containing current.sql file, which will be updated with latest ax_define function definitions.', string: true, }), handler: generate, };