@megaorm/cli
Version:
This package allows you to communicate with MegaORM via commands directly from the command line interface (CLI).
569 lines (424 loc) • 18 kB
Markdown
## MegaORM CLI
This package allows you to communicate with MegaORM via commands directly from the command line interface (CLI), with support for defining and executing custom commands.
## Table of Contents
1. **[Installation](#installation)**
2. **[MegaORM Config](#megaorm-config)**
3. **[MegaORM Executor](#megaorm-executor)**
4. **[Built-in Commands](#built-in-commands)**
5. **[Custom Commands](#custom-commands)**
6. **[Output Methods](#output-methods)**
## Installation
To install this package, run the following command:
```bash
npm install @megaorm/cli
```
> You should be familiar with [@megaorm/pool](https://github.com/megaorm/megaorm-pool) and [@megaorm/cluster](https://github.com/megaorm/megaorm-cluster).
## MegaORM Config
This package requires a configuration file `mega.config.js` at the root of your project. Below is a breakdown of the configuration options, their purposes, and examples.
### Required Options
1. `cluster`: A `MegaCluster` instance representing your database setup. It can contain multiple pools, with different drivers (e.g., `MySQL`, `PostgreSQL`, `SQLite`).
2. `default`: The name of the pool `MegaORM` will use for connections by default.
Here’s an example of a `mega.config.js` file:
```js
const { resolve } = require('path');
const { MegaCluster } = require('@megaorm/cluster');
const { MegaClusterPool } = require('@megaorm/cluster');
const { MySQL } = require('@megaorm/mysql');
const { PostgreSQL } = require('@megaorm/pg');
const { SQLite } = require('@megaorm/sqlite');
module.exports = {
// Default pool name MegaORM will use
default: 'mysql',
// Cluster setup with three database pools
cluster: new MegaCluster(
new MegaClusterPool(
'mysql', // Pool name
new MySQL({
host: 'localhost',
user: 'root',
password: 'root',
database: 'main',
})
),
new MegaClusterPool(
'pg', // Pool name
new PostgreSQL({
host: 'localhost',
user: 'postgres',
password: 'root',
database: 'main',
})
),
new MegaClusterPool(
'sqlite', // Pool name
new SQLite(resolve(__dirname, './database.sqlite'))
)
),
};
```
In this setup:
- `mysql` is the default pool to request connections from.
- The `cluster` contains three database pools: `mysql`, `pg`, and `sqlite`. Each pool is defined with its respective driver and connection details.
### Optional Options
1. `paths`: An object that specifies custom paths for MegaORM folders. If not provided, MegaORM uses the default paths.
| Property | Description | Default Path |
| ------------------ | ----------------------------------- | ------------------- |
| `paths.generators` | Path to your **generators** folder. | `<root>/generators` |
| `paths.seeders` | Path to your **seeders** folder. | `<root>/seeders` |
| `paths.models` | Path to your **models** folder. | `<root>/models` |
| `paths.commands` | Path to your **commands** folder. | `<root>/commands` |
2. `typescript`: An object to enable and configure TypeScript support.
| Property | Description | Default |
| -------------------- | ------------------------------------------------- | ------------- |
| `typescript.enabled` | Enable TypeScript support (boolean). | `false` |
| `typescript.src` | Path to the **source** folder when TS is enabled. | `<root>/src` |
| `typescript.dist` | Path to the **dist** folder when TS is enabled. | `<root>/dist` |
- When `typescript.enabled` is `false`, commands like `node mega add:model <table>` will add js files.
- When `typescript.enabled` is `true`, the same command will add TypeScript files.
Here’s a complete example combining all options:
```js
const { resolve } = require('path');
const { MegaCluster } = require('@megaorm/cluster');
const { MegaClusterPool } = require('@megaorm/cluster');
const { MySQL } = require('@megaorm/mysql');
const { PostgreSQL } = require('@megaorm/pg');
const { SQLite } = require('@megaorm/sqlite');
module.exports = {
default: 'mysql',
cluster: new MegaCluster(
new MegaClusterPool(
'mysql',
new MySQL({
host: 'localhost',
user: 'root',
password: 'root',
database: 'main',
})
),
new MegaClusterPool(
'pg',
new PostgreSQL({
host: 'localhost',
user: 'postgres',
password: 'root',
database: 'main',
})
),
new MegaClusterPool(
'sqlite',
new SQLite(resolve(__dirname, './database.sqlite'))
)
),
paths: {
generators: './gens', // Store generator files in gens folder
seeders: './seeders',
models: './models',
commands: './cmds', // Store command files in cmds folder
},
typescript: {
enabled: true, // Enable typescript support
src: './src',
dist: './dist',
},
};
```
> If typescript is enabled `paths` must be relative
### Loading Configuration
You can also load your `mega.config.js` file from anywhere in your project. This is very useful because it provides easy access to your `MegaCluster` and allows you to define custom options if needed.
Start by importing **MegaConfig** from `@megaorm/cli` into your project:
```js
import { MegaConfig } from '@megaorm/cli';
```
Now you can load `mega.config.js` from anywhere in your project using the `MegaConfig.load()` method. This method will return a promise that resolves with the configuration object.
```js
MegaConfig.load().then((config) => console.log(config));
```
- The `load` method:
- Loads the configuration from the `mega.config.js` file.
- Caches the configuration after the first load for better performance.
- Runs registered validators to ensure the configuration is valid.
### Loading Other Configurations
If you need to load a custom configuration file, you can extend [@megaorm/config](https://github.com/megaorm/megaorm-config) to create your own configuration loader. For example:
```js
// Import Config
const { Config } = require('@megaorm/config');
// Extend Config
class NodeJSConfig extends Config {
static file = 'package.json'; // Load package.json
static default = { version: '1.0.0' }; // Default package.json config
}
// Export NodeJSConfig
module.exports = { NodeJsConfig };
// Now you can use `NodeJSConfig` to load package.json anywhere you like
NodeJSConfig.load().then((config) => console.log(config));
// Get the project root from any sub-folder
NodeJSConfig.resolveSync();
// Load any JSON config
NodeJSConfig.loadJSON(path);
// Load any JS config
NodeJSConfig.loadJS(path);
```
## MegaORM Executor
Once your `mega.config.js` file is set up, you can start executing commands. Here's how:
1. Create a `mega.js` file in your root folder:
```bash
touch mega.js
```
2. Import `execute()` from `@megaorm/cli`:
```js
const { execute } = require('@megaorm/cli');
```
3. Call the `execute` method and handle errors:
```js
execute()
.then(() => process.exit()) // Stop the process
.catch((error) => console.error(error.message));
```
This setup allows you to interact with MegaORM via commands. For example:
```bash
node mega version
```
or
```bash
node mega v
```
Both commands will output the current version of MegaORM.
## Built-in Commands
Below is the full list of built-in commands:
| **Command** | **Description** |
| ------------------------------------- | ----------------------------------------------- |
| `node mega add:command <!name>` | Adds a command file. |
| `node mega add:cmd <!name>` | Shortcut for `add:command`. |
| `node mega add:model <!table>` | Adds a model file for a table. |
| `node mega add:seeder <!table>` | Adds a seeder file for a table. |
| `node mega add:generator <!table>` | Adds a generator file for a table. |
| `node mega add:gen <!table>` | Shortcut for `add:generator`. |
| `node mega add:for <!table>` | Adds generator, seeder, and model files. |
| `node mega remove:command <!name>` | Removes a command file. |
| `node mega remove:cmd <!name>` | Shortcut for `remove:command`. |
| `node mega remove:model <!table>` | Removes a model file. |
| `node mega remove:seeder <!table>` | Removes a seeder file. |
| `node mega remove:generator <!table>` | Removes a generator file. |
| `node mega remove:gen <!table>` | Shortcut for `remove:generator`. |
| `node mega remove:for <!table>` | Removes generator, seeder, and model files. |
| `node mega generate` | Executes generators and creates tables. |
| `node mega gen` | Shortcut for `generate`. |
| `node mega rollback` | Executes generators and drops tables. |
| `node mega roll` | Shortcut for `rollback`. |
| `node mega reset` | Drops all your tables. |
| `node mega seed <?table>` | Seeds all tables or a specific table. |
| `node mega clear <?table>` | Clears all tables or a specific table. |
| `node mega fetch <!table> <?id>` | Fetches data from a table, with an optional ID. |
| `node mega version` | Outputs MegaORM's current version. |
| `node mega v` | Shortcut for `version`. |
## Custom Commands
You can extend `MegaCommand` to create custom commands for your application. Here's a simple step-by-step guide to define custom commands like a pro:
### Adding New Command
To create a new command, run the following in your terminal:
```bash
node mega add:command GreetCommand
```
This creates a new file at: `./commands/GreetCommand.js`
### Template Overview
When you open the `GreetCommand.js` file, you'll see this:
```js
const { MegaCommand } = require('@megaorm/cli');
class GreetCommand extends MegaCommand {
static syntax = '';
static exec() {
// Your command logic here...
}
}
module.exports = { GreetCommand };
```
This is a basic template for building a command. Let's break it down:
1. `syntax`: Define what arguments or options your command accepts.
2. `exec()`: Implement the logic that runs when the command is executed.
### Command Syntax
The `syntax` property lets you describe the inputs for your command. Inputs can be:
- Required arguments: `<! name>`
- Optional arguments: `<? name>`
- Options (flags): `<- name>`
For example, let’s make a command that says:
- `"Hello There!"` if no name is given.
- `"Hello Name!"` if a name is provided.
Since the name is optional, the syntax is:
```js
syntax = '<? name>';
```
### Command Logic
The `exec()` method contains the code that runs when the command is executed.
Use `this.argument(name)` to get the value of arguments defined in the `syntax`. For example:
```js
exec() {
// Fetch the optional argument 'name'
const name = this.argument('name');
// If a name is provided
if (name) console.log(`Hello ${name}!`);
// If no name is provided
else console.log('Hello There!');
}
```
### Command Registration
To make your command executable, register it in the `mega.js` file in your project’s root:
1. Import the command:
```js
const { GreetCommand } = require('./commands/GreetCommand');
```
2. Register it with a name:
```js
register('greet', GreetCommand);
```
Your `mega.js` file will look like this:
```js
// Import modules
const { register, execute } = require('@megaorm/cli');
const { GreetCommand } = require('./commands/GreetCommand');
// Register commands
register('greet', GreetCommand);
// Execute commands
execute()
.then(() => process.exit())
.catch((error) => console.error(error.message));
```
### Command Execution
Run your command from the terminal:
```bash
node mega greet
# Outputs: "Hello There!"
node mega greet john
# Outputs: "Hello john!"
```
### Command Options
To add options (flags) to your command, include them in the syntax using `<- name>` and retrieve them with `this.option(name)`. Let’s add a `-lazy` option:
- If `-lazy` is used, the command says `"Hi!"` or `"Hi Name!"` instead of `"Hello"`.
```js
syntax = '<? name> <- lazy>';
```
```js
exec() {
const name = this.argument('name');
// Check if the -lazy option is used
const lazy = this.option('lazy');
if (lazy && name) console.log(`Hi ${name}!`);
else if (lazy) console.log('Hi!');
else if (name) console.log(`Hello ${name}!`);
else console.log('Hello There!');
}
```
### Command Input and Casting
All command-line inputs are treated as **strings**. So you should convert them as needed.
```js
const age = Number(this.argument('age')); // Convert '23' to 23
```
### More Command Examples
Here’s a more advanced example of a command that inserts a user into a database:
- Required arguments: `email`, `password`
- Optional argument: `age`
- Option: `-male` (indicates gender)
```js
syntax = '<! email> <! password> <? age> <- male>';
```
```js
exec() {
const email = this.argument('email');
const password = this.argument('password');
const gender = this.option('male') ? 'male' : 'female';
const age = this.argument('age')
? Number(this.argument(age))
: undefined;
// Insert the user into the database (simplified example)
console.log(
`email: ${email}
password: ${password}
age: ${age}
gender: ${gender}`
);
}
```
Here’s a practical example of how to use `MegaCommand` to insert a user into a database.
```js
const { MegaCommand } = require('@megaorm/cli');
const { MegaConfig } = require('@megaorm/cli');
class InsertUserCommand extends MegaCommand {
/**
* Define the command syntax, as the following:
* - `<! name>`: required argument
* - `<? name>`: optional argument
* - `<- name>`: option
*/
static syntax = '<! email> <! password> <? age> <- male>';
/**
* This method is called when the command is executed.
*
* @returns No return value is required.
*/
static async exec() {
// Reference arguments and options
const row = {
email: this.argument('email'),
password: this.argument('password'),
age: this.argument('age') ? Number(this.argument('age')) : undefined,
gender: this.option('male') ? 'male' : 'female',
};
// Build query
const keys = Object.keys(row).filter((k) => row[k] !== undefined);
const values = keys.map((k) => row[k]);
const placeholders = keys.map(() => '?').join(', ');
const columns = keys.join(', ');
const query = `INSERT INTO users (${columns}) VALUES (${placeholders});`;
// Load MegaORM Config
const config = await MegaConfig.load();
// Request a Connection
const connection = await config.cluster.request(config.default);
// Execute an Insert Query
await connection.query(query, values);
// Log Success Message
this.success(`User has been successfully created!`);
}
}
module.exports = { InsertUserCommand };
```
Register this command in your `mega.js` file:
```js
// Import modules
const { register, execute } = require('@megaorm/cli');
const { GreetCommand } = require('./commands/GreetCommand');
const { InsertUserCommand } = require('./commands/InsertUserCommand');
// Register commands
register('greet', GreetCommand);
register('insert:user', InsertUserCommand);
// Execute commands
execute()
.then(() => process.exit())
.catch((error) => console.error(error.message));
```
You can now use the command to insert a user into the database:
```bash
# Insert a user with email, password, age, and gender
node mega insert:user user1@example.com password 25 -male
# Insert a user with only required fields
node mega insert:user user2@example.com password
```
- **MegaCommand** makes it easy to define and execute custom commands.
- Use `syntax` to describe inputs and `exec()` to write logic.
- Register your command and run it from the terminal.
Now you're ready to build powerful commands in your app! 🚀
## Output Methods
The `MegaCommand` class provides built-in methods for displaying messages in the terminal, each with a specific color to represent the message type:
1. `this.success(message)`: **Green** color is used for success messages, indicating that an operation has completed successfully.
```js
this.success('User created successfully!');
```
2. `this.info(message)`: **Blue** color is used for informational messages, providing helpful details or updates without indicating any issue.
```js
this.info('Current MegaORM version is 1.0.0');
```
3. `this.warning(message)`: **Yellow** color is used for warning messages, alerting the user to something that might require attention or could cause problems later.
```js
this.warning('Deprecated option, please update your configuration.');
```
4. `this.error(message)`: **Red** color is used for error messages, signaling that something went wrong and needs immediate attention.
```js
this.error('Invalid email address provided.');
```