Created
February 16, 2023 10:25
-
-
Save rbideau/711e77240e30c178da76e8953b6fd15f to your computer and use it in GitHub Desktop.
Fix enum statements in TypeORM migration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
// This fix TypeORM enum creation, which generate multiple CREATE TYPE statement | |
// when an enum is used in multiple columns. | |
// The solution is taken from this SO answer: https://stackoverflow.com/a/48382296 | |
// which gracefully handle the exception | |
// - https://github.com/typeorm/typeorm/issues/5648 | |
// - https://github.com/typeorm/typeorm/issues/5738 | |
// - https://github.com/typeorm/typeorm/issues/7501 | |
import { existsSync, readFileSync, writeFileSync } from 'node:fs'; | |
import { resolve } from 'node:path'; | |
import { argv, cwd, exit } from 'node:process'; | |
/** | |
* Extract arguments from `argv`, validate filePath exists | |
* | |
* @returns {object} Contains `filePath` | |
*/ | |
function extractArguments() { | |
// Validate argv | |
if (argv.length !== 3) { | |
console.log('Usage: yarn run db:migration:fix-enum <RELATIVE_FILE_PATH>'); | |
exit(1); | |
} | |
// Extract arguments | |
const [, , filePath] = argv; | |
// Validate filePath exists | |
const absoluteFilePath = resolve(cwd(), filePath); | |
if (!existsSync(absoluteFilePath)) { | |
console.log(`file at ${absoluteFilePath} doesn't exists`); | |
exit(1); | |
} | |
console.log('arguments:'); | |
console.log(`- filePath=${filePath}`); | |
return { absoluteFilePath }; | |
} | |
/** | |
* Wrap all CREATE TYPE statement in `content` with DO/EXCEPTION | |
* to ignore exception when the type already exists | |
* | |
* @see https://stackoverflow.com/a/48382296 | |
* @param {String} content | |
* @returns {String} | |
*/ | |
function fixEnumCreateStatement(content) { | |
const regex = | |
/await queryRunner.query\(`\s+(CREATE TYPE ["',a-zA-Z_\s\(\.]+\))\s+`\);/g; | |
console.log(`Fix CREATE statement:`); | |
const replaceCreateTypeStatement = (match, createStatement) => { | |
console.log(` ${createStatement.trim().replaceAll(/\s+/g, ' ')}`); | |
return `await queryRunner.query(\` | |
DO $$ BEGIN | |
${createStatement}; | |
EXCEPTION | |
WHEN duplicate_object THEN null; | |
END $$; | |
\`);`; | |
}; | |
const fixed = content.replaceAll(regex, replaceCreateTypeStatement); | |
return fixed; | |
} | |
/** | |
* Wrap all ALTER TYPE statement in `content` with DO/EXCEPTION | |
* to ignore exception when the type name already exists | |
* | |
* @param {String} content | |
* @returns {String} | |
*/ | |
function fixEnumAlterStatement(content) { | |
const regex = | |
/await queryRunner.query\(`\s+(ALTER TYPE ["a-zA-Z_\s\.]+)\s+`\);/g; | |
console.log(`Fix ALTER statement:`); | |
const replaceCreateTypeStatement = (match, alterStatement) => { | |
console.log(` ${alterStatement.trim().replaceAll(/\s+/g, ' ')}`); | |
return `await queryRunner.query(\` | |
DO $$ BEGIN | |
${alterStatement}; | |
EXCEPTION | |
WHEN duplicate_object THEN null; | |
END $$; | |
\`);`; | |
}; | |
const fixed = content.replaceAll(regex, replaceCreateTypeStatement); | |
return fixed; | |
} | |
/** | |
* Wrap all DROP TYPE statement in `content` with DO/EXCEPTION | |
* to ignore exception when the type is used in other table | |
* | |
* @param {String} content | |
* @returns {String} | |
*/ | |
function fixEnumDropStatement(content) { | |
const regex = | |
/await queryRunner.query\(`\s+(DROP TYPE ["a-zA-Z_\s\.]+)\s+`\);/g; | |
console.log(`Fix DROP statement:`); | |
const replaceCreateTypeStatement = (match, dropStatement) => { | |
console.log(` ${dropStatement.trim()}`); | |
return `await queryRunner.query(\` | |
DO $$ BEGIN | |
${dropStatement}; | |
EXCEPTION | |
WHEN dependent_objects_still_exist THEN null; | |
END $$; | |
\`);`; | |
}; | |
const fixed = content.replaceAll(regex, replaceCreateTypeStatement); | |
return fixed; | |
} | |
// RUN | |
const { absoluteFilePath } = extractArguments(); | |
let content = readFileSync(absoluteFilePath, { encoding: 'utf-8' }); | |
content = fixEnumCreateStatement(content); | |
content = fixEnumAlterStatement(content); | |
content = fixEnumDropStatement(content); | |
writeFileSync(absoluteFilePath, content); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment