#!/usr/bin/env node const fs = require('fs'); const path = require('path'); // Function to generate a timestamp for the migration filename function generateTimestamp() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); const seconds = String(now.getSeconds()).padStart(2, '0'); return `${year}${month}${day}${hours}${minutes}${seconds}`; } // Function to parse SQL CREATE TABLE statement function parseCreateTable(sql) { // Extract table name const tableNameMatch = sql.match(/CREATE TABLE `([^`]+)`/i); if (!tableNameMatch) { throw new Error('Could not find table name in SQL statement'); } const tableName = tableNameMatch[1]; // Extract column definitions const columnsSection = sql.match(/\(([^)]+)\)/)[1]; // Split by commas, but be careful with commas inside parentheses (for default values) let depth = 0; let columns = []; let currentColumn = ''; for (let i = 0; i < columnsSection.length; i++) { const char = columnsSection[i]; if (char === '(') depth++; if (char === ')') depth--; if (char === ',' && depth === 0) { columns.push(currentColumn.trim()); currentColumn = ''; } else { currentColumn += char; } } if (currentColumn.trim()) { columns.push(currentColumn.trim()); } // Filter out non-column definitions (like PRIMARY KEY) const columnDefs = columns.filter(col => col.trim().startsWith('`')); // Extract primary key const primaryKeyMatch = columnsSection.match(/PRIMARY KEY \(`([^`]+)`\)/i); const primaryKey = primaryKeyMatch ? primaryKeyMatch[1] : null; // Extract table options const engineMatch = sql.match(/ENGINE=(\w+)/i); const engine = engineMatch ? engineMatch[1] : null; const charsetMatch = sql.match(/CHARSET=([^\s]+)/i); const charset = charsetMatch ? charsetMatch[1] : null; const collateMatch = sql.match(/COLLATE=([^\s]+)/i); const collate = collateMatch ? collateMatch[1] : null; const commentMatch = sql.match(/COMMENT='([^']+)'/i); const comment = commentMatch ? commentMatch[1] : null; return { tableName, columns: columnDefs, primaryKey, engine, charset, collate, comment }; } // Function to convert SQL column definition to Knex column builder function convertColumnToKnex(columnDef) { // Extract column name const nameMatch = columnDef.match(/`([^`]+)`/); if (!nameMatch) return null; const name = nameMatch[1]; // Determine column type let type = ''; let knexType = ''; if (columnDef.includes('varchar')) { const sizeMatch = columnDef.match(/varchar\((\d+)\)/i); const size = sizeMatch ? parseInt(sizeMatch[1]) : 255; type = `varchar(${size})`; knexType = `string('${name}', ${size})`; } else if (columnDef.includes('datetime')) { type = 'datetime'; knexType = `datetime('${name}')`; } else if (columnDef.includes('tinyint')) { type = 'tinyint'; knexType = `boolean('${name}')`; } else { // Default to string if type is not recognized knexType = `string('${name}')`; } // Check for NOT NULL const isNullable = !columnDef.includes('NOT NULL'); if (isNullable) { knexType += '.nullable()'; } // Check for DEFAULT value const defaultMatch = columnDef.match(/DEFAULT\s+(?:'([^']+)'|(\d+))/i); if (defaultMatch) { const defaultValue = defaultMatch[1] || defaultMatch[2]; if (type === 'tinyint') { knexType += `.defaultTo(${defaultValue})`; } else { knexType += `.defaultTo('${defaultValue}')`; } } // Check for character set and collation if (columnDef.includes('CHARACTER SET') || columnDef.includes('COLLATE')) { const charsetMatch = columnDef.match(/CHARACTER SET\s+([^\s]+)/i); const collateMatch = columnDef.match(/COLLATE\s+([^\s]+)/i); if (charsetMatch) { knexType += `.charset('${charsetMatch[1]}')`; } if (collateMatch) { knexType += `.collate('${collateMatch[1]}')`; } } return knexType; } // Function to generate Knex migration file content function generateKnexMigration(parsedTable) { const { tableName, columns, primaryKey, engine, charset, collate, comment } = parsedTable; let knexColumns = columns.map(convertColumnToKnex).filter(Boolean); let migrationContent = `exports.up = function(knex) { return knex.schema.createTable('${tableName}', function(table) { ${knexColumns.map(col => ` table.${col};`).join('\n')} `; if (primaryKey) { migrationContent += ` table.primary(['${primaryKey}']);\n`; } if (engine || charset || collate || comment) { migrationContent += ` // Table options\n`; if (comment) { migrationContent += ` table.comment('${comment}');\n`; } } migrationContent += ` })`; if (engine || charset || collate) { migrationContent += `.options({`; const options = []; if (engine) options.push(`engine: '${engine}'`); if (charset) options.push(`charset: '${charset}'`); if (collate) options.push(`collate: '${collate}'`); migrationContent += `\n ${options.join(',\n ')}\n })`; } migrationContent += `;\n}; exports.down = function(knex) { return knex.schema.dropTable('${tableName}'); }; `; return migrationContent; } // Main function function main() { // Get SQL from command line argument or stdin let sql = ''; if (process.argv.length > 2) { // Read from file if provided const filePath = process.argv[2]; sql = fs.readFileSync(filePath, 'utf8'); } else { return console.log('No file provided'); } try { const parsedTable = parseCreateTable(sql); const migrationContent = generateKnexMigration(parsedTable); // Generate filename with timestamp const timestamp = generateTimestamp(); const filename = `${timestamp}_create_${parsedTable.tableName}_table.js`; // Write to file fs.writeFileSync(filename, migrationContent); console.log(`Migration file created: ${filename}`); // Also output to console console.log('\nMigration content:'); console.log(migrationContent); } catch (error) { console.error('Error:', error.message); } } main();