UNPKG

lipgrate

Version:

Lipgrate is a clean and safe migration toolkit for SQL databases. Designed to be readable, minimal, and powerful.

184 lines (159 loc) 7.12 kB
const fs = require('fs'); const path = require('path'); const logger = require('../common/logger'); const chalk = require('chalk'); const defaultConfigContent = `// Lipgrate Configuration File // // This file contains example configurations for PostgreSQL, MySQL, and SQLite. // Choose the configuration that matches your database and assign it to module.exports. // 1. PostgreSQL Configuration const postgresql = { client: 'postgresql', connection: { host: 'localhost', user: 'your_pg_user', password: 'your_pg_password', database: 'your_pg_db', port: 5432, }, migrations: { directory: './migrations/postgresql' }, seeds: { directory: './migrations/seeds' } }; // 2. MySQL Configuration const mysql = { client: 'mysql', connection: { host: 'localhost', user: 'your_mysql_user', password: 'your_mysql_password', database: 'your_mysql_db', port: 3306, }, migrations: { directory: './migrations/mysql' }, seeds: { directory: './migrations/seeds' } }; // 3. SQLite Configuration const sqlite = { client: 'sqlite', connection: { filename: './lipgrate.db' }, migrations: { directory: './migrations/sqlite' }, seeds: { directory: './migrations/seeds' } }; // --- EXPORT YOUR CHOSEN CONFIGURATION --- // // Select the configuration you want to use by assigning it to module.exports. // By default, PostgreSQL is selected. module.exports = postgresql; // To use MySQL, comment out the line above and uncomment the line below: // module.exports = mysql; // To use SQLite, comment out the lines above and uncomment the line below: // module.exports = sqlite; `; const configFileName = 'migrator.config.js'; async function execute() { let createdSomething = false; // 1. Handle config file creation const configPath = path.resolve(process.cwd(), configFileName); if (fs.existsSync(configPath)) { logger.warning(`Configuration file '${configFileName}' already exists.`); } else { fs.writeFileSync(configPath, defaultConfigContent); logger.success(`✔ Created configuration file: ${configFileName}`); createdSomething = true; } // 2. Handle migrations directory creation const migrationsDir = path.resolve(process.cwd(), 'migrations'); if (fs.existsSync(migrationsDir)) { logger.warning(`Migrations directory 'migrations' already exists.`); } else { fs.mkdirSync(migrationsDir, { recursive: true }); logger.success(`✔ Created migrations directory: migrations/`); // Create subdirectories for each database type const dbDirs = ['postgresql', 'mysql', 'sqlite']; dbDirs.forEach(dir => { const dbDirPath = path.join(migrationsDir, dir); if (!fs.existsSync(dbDirPath)) { fs.mkdirSync(dbDirPath); logger.success(` ✔ Created subdirectory: migrations/${dir}/`); } }); createExampleMigrations(migrationsDir); createdSomething = true; } // 3. Handle seeds directory creation const seedsPath = path.join('migrations', 'seeds'); const seedsDir = path.join(process.cwd(), seedsPath); if (fs.existsSync(seedsDir)) { logger.warning(`Seeds directory '${seedsPath}' already exists.`); } else { fs.mkdirSync(seedsDir, { recursive: true }); logger.success(`✔ Created seeds directory: ${seedsPath}`); const exampleSeedFile = path.join(seedsDir, '01_example_seed.js'); const seedContent = `/** * This is an example seed file. * Seed files are simple JavaScript modules that export an async 'run' function. * * @param {object} db - The database adapter instance. */ exports.run = async function (db) { // It's good practice to log what your seed is doing. console.log('Seeding initial data...'); // Use 'await' for all database operations. // Use parameterized queries (the '?' placeholders) to prevent SQL injection. // This example assumes you have a table that can accept this data. // Example: // await db.query( // 'INSERT INTO products (name, price) VALUES (?, ?), (?, ?)', // ['Laptop Pro', 1499.99, 'Wireless Mouse', 49.99] // ); console.log('Finished seeding.'); };`; fs.writeFileSync(exampleSeedFile, seedContent); logger.success('✔ Created example seed file.'); createdSomething = true; } // Display final message if anything was created if (createdSomething) { logger.success('\nLipgrate initialized successfully!'); logger.info('Next steps:\n 1. Edit migrator.config.js to match your database credentials.\n 2. Create your first migration: lipgrate create <migration_name>\n 3. Run migrations: lipgrate migrate\n 4. Seed your database: lipgrate seed'); } else { logger.info('Everything is already set up. You can start creating migrations.'); } } const examplePostgres = `// Example migration for PostgreSQL\n// Creates a 'users' table and an index on the email column.\n\nexports.up = [\n {\n createTable: {\n name: 'users',\n columns: {\n id: 'serial:primary',\n email: 'varchar(255):notNullable:unique',\n password_hash: 'varchar(255):notNullable',\n created_at: 'timestamptz:default(now())',\n updated_at: 'timestamptz:default(now())'\n }\n }\n },\n {\n addIndex: {\n table: 'users',\n columns: 'email',\n name: 'users_email_idx'\n }\n }\n];\n\nexports.down = [\n {\n dropIndex: {\n name: 'users_email_idx'\n }\n },\n {\n dropTable: 'users'\n }\n];`; const exampleMySql = `// Example migration for MySQL\n// Creates a 'products' table with timestamps.\n\nexports.up = {\n createTable: {\n name: 'products',\n columns: {\n id: 'increments',\n name: 'string:notNullable',\n description: 'text',\n price: 'decimal(10, 2):notNullable'\n },\n options: {\n timestamps: true // Automatically adds created_at and updated_at\n }\n }\n};\n\nexports.down = {\n dropTable: 'products'\n};`; const exampleSqlite = `// Example migration for SQLite\n// Creates a 'posts' table.\n\nexports.up = {\n createTable: {\n name: 'posts',\n columns: {\n id: 'integer:primary:autoincrement',\n title: 'string:notNullable',\n body: 'text',\n author_id: 'integer'\n }\n }\n};\n\nexports.down = {\n dropTable: 'posts'\n};`; function createExampleMigrations(migrationsDir) { const examples = [ { dir: 'postgresql', name: '001_postgres_example.js.example', content: examplePostgres }, { dir: 'mysql', name: '002_mysql_example.js.example', content: exampleMySql }, { dir: 'sqlite', name: '003_sqlite_example.js.example', content: exampleSqlite }, ]; try { examples.forEach(example => { const dirPath = path.join(migrationsDir, example.dir); const filePath = path.join(dirPath, example.name); if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, example.content); } }); logger.success('✔ Created example migration files in their respective directories.'); } catch (error) { logger.warning('Could not create example migration files.'); } } module.exports = { execute };