@bayajidalam/apollo-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Apollo CLI
2
+
3
+ A CLI tool for scaffolding and managing Apollo backend projects (Express + TypeScript + Prisma + PostgreSQL).
4
+
5
+ ## Installation
6
+
7
+ ### Global Installation
8
+
9
+ To install the CLI globally:
10
+
11
+ ```bash
12
+ npm install -g apollo-cli
13
+ ```
14
+
15
+ ### Local Development Setup
16
+
17
+ If you are developing this tool locally and want to test it:
18
+
19
+ 1. **Build the CLI**:
20
+ ```bash
21
+ npm run build
22
+ ```
23
+
24
+ 2. **Link it globally**:
25
+ ```bash
26
+ npm link
27
+ ```
28
+ This makes the `apollo-cli` command available in your terminal.
29
+
30
+ ## Usage
31
+
32
+ ### 1. `generate` (alias: `g`)
33
+
34
+ Generates a new module structure including controller, service, route, interface, validation, and constants.
35
+
36
+ **Syntax:**
37
+ ```bash
38
+ apollo-cli generate module <ModuleName>
39
+ # OR
40
+ apollo-cli g module <ModuleName>
41
+ ```
42
+
43
+ **Example:**
44
+ Generate a `User` module:
45
+ ```bash
46
+ apollo-cli g module User
47
+ ```
48
+
49
+ This will create the following files in `src/app/modules/User`:
50
+ - `user.controller.ts`
51
+ - `user.service.ts`
52
+ - `user.route.ts`
53
+ - `user.interface.ts`
54
+ - `user.validation.ts`
55
+ - `user.constant.ts`
56
+
57
+ ### 2. `build`
58
+
59
+ Builds the application for production deployment.
60
+
61
+ **Syntax:**
62
+ ```bash
63
+ apollo-cli build
64
+ ```
65
+
66
+ **What it does:**
67
+ - Cleans the `dist` folder.
68
+ - Compiles TypeScript files.
69
+ - Copies `package.json`, `package-lock.json`, and `.env` to the `dist` folder.
70
+
71
+ ### 3. `prisma`
72
+
73
+ Helper commands for Prisma ORM.
74
+
75
+ **Generate Client:**
76
+ ```bash
77
+ apollo-cli prisma generate
78
+ ```
79
+ *(Runs `npx prisma generate`)*
80
+
81
+ **Run Migrations:**
82
+ ```bash
83
+ apollo-cli prisma migrate
84
+ ```
85
+ *(Runs `npx prisma migrate dev`)*
86
+
87
+ ## Development
88
+
89
+ To run the CLI directly from source without building:
90
+
91
+ ```bash
92
+ npx ts-node src/bin/index.ts <command>
93
+ ```
94
+ Example:
95
+ ```bash
96
+ npx ts-node src/bin/index.ts generate module Product
97
+ ```
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const generate_1 = require("../commands/generate");
6
+ const build_1 = require("../commands/build");
7
+ const prisma_1 = require("../commands/prisma");
8
+ const init_1 = require("../commands/init");
9
+ const program = new commander_1.Command();
10
+ program
11
+ .version('1.0.0')
12
+ .description('Apollo CLI Tool');
13
+ program.addCommand(generate_1.generateCommand);
14
+ program.addCommand(build_1.buildCommand);
15
+ program.addCommand(prisma_1.prismaCommand);
16
+ program.addCommand(init_1.initCommand);
17
+ program.parse(process.argv);
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const child_process_1 = require("child_process");
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ exports.buildCommand = new commander_1.Command('build')
13
+ .description('Build the application for production')
14
+ .action(async () => {
15
+ console.log(chalk_1.default.blue('šŸš€ Starting build process...'));
16
+ try {
17
+ // 1. Clean dist folder
18
+ const distPath = path_1.default.join(process.cwd(), 'dist');
19
+ if (fs_extra_1.default.existsSync(distPath)) {
20
+ console.log(chalk_1.default.yellow('🧹 Cleaning dist folder...'));
21
+ await fs_extra_1.default.remove(distPath);
22
+ }
23
+ // 2. Run TypeScript Compiler
24
+ console.log(chalk_1.default.blue('šŸ”Ø Compiling TypeScript...'));
25
+ (0, child_process_1.execSync)('npx tsc', { stdio: 'inherit' });
26
+ // 3. Copy Key Files
27
+ console.log(chalk_1.default.blue('šŸ“‚ Copying static files...'));
28
+ const filesToCopy = ['package.json', 'package-lock.json', '.env'];
29
+ for (const file of filesToCopy) {
30
+ const srcPath = path_1.default.join(process.cwd(), file);
31
+ if (fs_extra_1.default.existsSync(srcPath)) {
32
+ await fs_extra_1.default.copy(srcPath, path_1.default.join(distPath, file));
33
+ }
34
+ }
35
+ console.log(chalk_1.default.green('\nāœ… Build completed successfully!'));
36
+ console.log(chalk_1.default.white('To deploy, upload the "dist" folder and run:'));
37
+ console.log(chalk_1.default.cyan('npm install --production'));
38
+ }
39
+ catch (error) {
40
+ console.error(chalk_1.default.red('āŒ Build failed:'), error);
41
+ process.exit(1);
42
+ }
43
+ });
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const generator_1 = require("../utils/generator");
11
+ exports.generateCommand = new commander_1.Command('generate')
12
+ .alias('g')
13
+ .description('Generate a new resource')
14
+ .argument('[type]', 'Type of resource to generate (e.g., module)')
15
+ .argument('[name]', 'Name of the resource')
16
+ .action(async (type, name) => {
17
+ // 1. Validate Input
18
+ if (!type) {
19
+ const answers = await inquirer_1.default.prompt([{
20
+ type: 'list',
21
+ name: 'type',
22
+ message: 'What would you like to generate?',
23
+ choices: ['module']
24
+ }]);
25
+ type = answers.type;
26
+ }
27
+ if (type !== 'module') {
28
+ console.error('Only "module" generation is currently supported.');
29
+ return;
30
+ }
31
+ if (!name) {
32
+ const answers = await inquirer_1.default.prompt([{
33
+ type: 'input',
34
+ name: 'name',
35
+ message: 'What is the name of the module?',
36
+ validate: (input) => input.trim() !== '' ? 'Module name is required' : true
37
+ }]);
38
+ name = answers.name;
39
+ }
40
+ const moduleNameRaw = name.trim();
41
+ // 2. Format Casing
42
+ const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
43
+ const camelize = (s) => s.charAt(0).toLowerCase() + s.slice(1);
44
+ const moduleNamePascal = capitalize(moduleNameRaw);
45
+ const moduleNameCamel = camelize(moduleNameRaw);
46
+ console.log(`Generating module: ${moduleNamePascal}...`);
47
+ // 3. Define Output Paths
48
+ // Assume we are in the project root
49
+ const baseDir = path_1.default.join(process.cwd(), 'src/app/modules', moduleNamePascal);
50
+ // 4. Generate Files
51
+ const files = [
52
+ { template: 'module/controller.ejs', output: `${moduleNameCamel}.controller.ts` },
53
+ { template: 'module/service.ejs', output: `${moduleNameCamel}.service.ts` },
54
+ { template: 'module/route.ejs', output: `${moduleNameCamel}.route.ts` },
55
+ { template: 'module/interface.ejs', output: `${moduleNameCamel}.interface.ts` },
56
+ { template: 'module/validation.ejs', output: `${moduleNameCamel}.validation.ts` },
57
+ { template: 'module/constant.ejs', output: `${moduleNameCamel}.constant.ts` },
58
+ ];
59
+ for (const file of files) {
60
+ await (0, generator_1.renderTemplate)(file.template, path_1.default.join(baseDir, file.output), { moduleName: moduleNamePascal, camelModuleName: moduleNameCamel });
61
+ }
62
+ // 5. Success Message
63
+ console.log('\nModule generated successfully! šŸš€');
64
+ });
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const child_process_1 = require("child_process");
13
+ exports.initCommand = new commander_1.Command('init')
14
+ .description('Initialize a new Apollo Gears project')
15
+ .argument('[projectName]', 'Name of the project directory')
16
+ .action(async (projectName) => {
17
+ // 1. Prompt for Project Name if missing
18
+ if (!projectName) {
19
+ const answers = await inquirer_1.default.prompt([
20
+ {
21
+ type: 'input',
22
+ name: 'name',
23
+ message: 'What is the name of your project?',
24
+ default: 'my-apollo-app',
25
+ validate: (input) => input.trim() !== '' ? true : 'Project name is required'
26
+ }
27
+ ]);
28
+ projectName = answers.name;
29
+ }
30
+ const projectRoot = path_1.default.join(process.cwd(), projectName);
31
+ if (fs_extra_1.default.existsSync(projectRoot)) {
32
+ console.error(chalk_1.default.red(`Error: Directory "${projectName}" already exists.`));
33
+ process.exit(1);
34
+ }
35
+ console.log(chalk_1.default.blue(`\nšŸš€ Initializing project in ${projectRoot}...\n`));
36
+ try {
37
+ // 2. Create Directory
38
+ await fs_extra_1.default.ensureDir(projectRoot);
39
+ // 3. Create Package.json
40
+ const packageJson = {
41
+ name: projectName,
42
+ version: "1.0.0",
43
+ description: "Backend project generated by Apollo Gears CLI",
44
+ main: "dist/server.js",
45
+ scripts: {
46
+ "start": "node dist/server.js",
47
+ "dev": "ts-node-dev --respawn --transpile-only src/server.ts",
48
+ "build": "tsc",
49
+ "lint": "eslint src/**/*.ts",
50
+ "lint:fix": "eslint src/**/*.ts --fix",
51
+ "format": "prettier --write ."
52
+ },
53
+ keywords: ["express", "prisma", "typescript", "apollo-gears"],
54
+ author: "",
55
+ license: "ISC",
56
+ dependencies: {
57
+ "express": "^4.18.2",
58
+ "cors": "^2.8.5",
59
+ "dotenv": "^16.3.1",
60
+ "http-status": "^1.7.3",
61
+ "@prisma/client": "^5.7.1"
62
+ },
63
+ devDependencies: {
64
+ "typescript": "^5.3.3",
65
+ "ts-node-dev": "^2.0.0",
66
+ "@types/node": "^20.10.4",
67
+ "@types/express": "^4.17.21",
68
+ "@types/cors": "^2.8.17",
69
+ "prisma": "^5.7.1",
70
+ "eslint": "^8.55.0",
71
+ "prettier": "^3.1.1"
72
+ }
73
+ };
74
+ await fs_extra_1.default.writeJson(path_1.default.join(projectRoot, 'package.json'), packageJson, { spaces: 2 });
75
+ // 4. Create tsconfig.json
76
+ const tsconfig = {
77
+ "compilerOptions": {
78
+ "target": "ES2021",
79
+ "module": "commonjs",
80
+ "moduleResolution": "node",
81
+ "outDir": "./dist",
82
+ "rootDir": "./src",
83
+ "strict": true,
84
+ "esModuleInterop": true,
85
+ "skipLibCheck": true,
86
+ "forceConsistentCasingInFileNames": true
87
+ },
88
+ "include": ["src/**/*"],
89
+ "exclude": ["node_modules"]
90
+ };
91
+ await fs_extra_1.default.writeJson(path_1.default.join(projectRoot, 'tsconfig.json'), tsconfig, { spaces: 2 });
92
+ // 5. Create Scaffolding Folders
93
+ const folders = [
94
+ 'src/app/modules',
95
+ 'src/app/middlewares',
96
+ 'src/app/routes',
97
+ 'src/app/utils',
98
+ 'src/app/errors',
99
+ 'src/app/config'
100
+ ];
101
+ for (const folder of folders) {
102
+ await fs_extra_1.default.ensureDir(path_1.default.join(projectRoot, folder));
103
+ }
104
+ // 6. Create Essential Files
105
+ // src/server.ts
106
+ const serverContent = `import app from './app';
107
+ import config from './app/config';
108
+
109
+ async function main() {
110
+ try {
111
+ app.listen(config.port, () => {
112
+ console.log(\`Example app listening on port \${config.port}\`);
113
+ });
114
+ } catch (err) {
115
+ console.log(err);
116
+ }
117
+ }
118
+
119
+ main();`;
120
+ await fs_extra_1.default.writeFile(path_1.default.join(projectRoot, 'src/server.ts'), serverContent);
121
+ // src/app.ts
122
+ const appContent = `import express, { Application, Request, Response } from 'express';
123
+ import cors from 'cors';
124
+
125
+ const app: Application = express();
126
+
127
+ // parsers
128
+ app.use(express.json());
129
+ app.use(cors());
130
+
131
+ // application routes
132
+ // app.use('/api/v1', router);
133
+
134
+ app.get('/', (req: Request, res: Response) => {
135
+ res.send('Hello from Apollo Gears World!');
136
+ });
137
+
138
+ export default app;`;
139
+ await fs_extra_1.default.writeFile(path_1.default.join(projectRoot, 'src/app.ts'), appContent);
140
+ // src/app/config/index.ts
141
+ const configContent = `import dotenv from 'dotenv';
142
+ import path from 'path';
143
+
144
+ dotenv.config({ path: path.join(process.cwd(), '.env') });
145
+
146
+ export default {
147
+ port: process.env.PORT,
148
+ database_url: process.env.DATABASE_URL,
149
+ };`;
150
+ await fs_extra_1.default.writeFile(path_1.default.join(projectRoot, 'src/app/config/index.ts'), configContent);
151
+ // .env
152
+ const envContent = `PORT=5000
153
+ DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"`;
154
+ await fs_extra_1.default.writeFile(path_1.default.join(projectRoot, '.env'), envContent);
155
+ // .gitignore
156
+ const gitignoreContent = `node_modules
157
+ dist
158
+ .env`;
159
+ await fs_extra_1.default.writeFile(path_1.default.join(projectRoot, '.gitignore'), gitignoreContent);
160
+ console.log(chalk_1.default.green('\nāœ… Project structure created successfully!'));
161
+ // 7. Install Dependencies Prompt
162
+ const { install } = await inquirer_1.default.prompt([
163
+ {
164
+ type: 'confirm',
165
+ name: 'install',
166
+ message: 'Would you like to install dependencies now?',
167
+ default: true
168
+ }
169
+ ]);
170
+ if (install) {
171
+ console.log(chalk_1.default.yellow('\nšŸ“¦ Installing dependencies...'));
172
+ (0, child_process_1.execSync)('npm install', { cwd: projectRoot, stdio: 'inherit' });
173
+ console.log(chalk_1.default.green('\nāœ… Dependencies installed!'));
174
+ }
175
+ console.log(chalk_1.default.cyan(`\nTo get started:\n cd ${projectName}\n ${install ? 'npm run dev' : 'npm install && npm run dev'}\n`));
176
+ }
177
+ catch (error) {
178
+ console.error(chalk_1.default.red('āŒ Initialization failed:'), error);
179
+ process.exit(1);
180
+ }
181
+ });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prismaCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const child_process_1 = require("child_process");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ exports.prismaCommand = new commander_1.Command('prisma')
11
+ .description('Prisma utility commands')
12
+ .argument('<action>', 'Action to perform (generate, migrate)')
13
+ .action(async (action) => {
14
+ try {
15
+ if (action === 'generate') {
16
+ console.log(chalk_1.default.blue('šŸ”„ Generating Prisma Client...'));
17
+ (0, child_process_1.execSync)('npx prisma generate', { stdio: 'inherit' });
18
+ console.log(chalk_1.default.green('āœ… Prisma Client generated!'));
19
+ }
20
+ else if (action === 'migrate') {
21
+ console.log(chalk_1.default.blue('šŸ”„ Running Migrations...'));
22
+ (0, child_process_1.execSync)('npx prisma migrate dev', { stdio: 'inherit' });
23
+ console.log(chalk_1.default.green('āœ… Migrations applied!'));
24
+ }
25
+ else {
26
+ console.error(chalk_1.default.red(`Unknown action: ${action}`));
27
+ console.log('Available actions: generate, migrate');
28
+ }
29
+ }
30
+ catch (error) {
31
+ // Error is already logged by inherit stdio usually, but we catch to prevent crash dump
32
+ console.error(chalk_1.default.red('āŒ Prisma command failed.'));
33
+ process.exit(1);
34
+ }
35
+ });
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderTemplate = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ejs_1 = __importDefault(require("ejs"));
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const renderTemplate = async (templateName, targetPath, data) => {
12
+ try {
13
+ // Resolve template path - assuming templates are in src/templates or dist/templates
14
+ // We look up from the current file location
15
+ const templateDir = path_1.default.resolve(__dirname, '../../src/templates');
16
+ const templatePath = path_1.default.join(templateDir, templateName);
17
+ // Fallback for dist structure if src not found (pseudo-logic for now, ideally strictly copy them)
18
+ // If running in ts-node, __dirname is src/utils, so ../templates works if we are at src
19
+ // Let's rely on standard path resolution relative to this file
20
+ // Check if template exists
21
+ if (!fs_extra_1.default.existsSync(templatePath)) {
22
+ // Try looking in dist if we are in dist
23
+ const distTemplatePath = path_1.default.join(__dirname, '../templates', templateName);
24
+ if (!fs_extra_1.default.existsSync(distTemplatePath)) {
25
+ console.error(chalk_1.default.red(`Template not found at: ${templatePath}`));
26
+ return;
27
+ }
28
+ }
29
+ const templateContent = await fs_extra_1.default.readFile(templatePath, 'utf-8');
30
+ const renderedContent = ejs_1.default.render(templateContent, data);
31
+ // Create directory if it doesn't exist
32
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(targetPath));
33
+ // Write file
34
+ await fs_extra_1.default.writeFile(targetPath, renderedContent);
35
+ console.log(chalk_1.default.green(`āœ“ Created: ${path_1.default.relative(process.cwd(), targetPath)}`));
36
+ }
37
+ catch (error) {
38
+ console.error(chalk_1.default.red('Error generating file:'), error);
39
+ process.exit(1);
40
+ }
41
+ };
42
+ exports.renderTemplate = renderTemplate;
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@bayajidalam/apollo-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool for speed up backend projects",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "apollo-cli": "dist/bin/index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "ts-node src/index.ts",
12
+ "build": "tsc",
13
+ "watch": "tsc -w",
14
+ "test": "echo \"Error: no test specified\" && exit 1",
15
+ "cli": "ts-node src/bin/index.ts"
16
+ },
17
+ "keywords": [
18
+ "cli",
19
+ "express",
20
+ "prisma",
21
+ "postgres",
22
+ "typescript"
23
+ ],
24
+ "author": "Bayajid Alam Joyel",
25
+ "repository": {
26
+ "url": "https://github.com/BayajidAlam/apollo-cli"
27
+ },
28
+ "license": "ISC",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "chalk": "^4.1.2",
34
+ "commander": "^13.1.0",
35
+ "ejs": "^3.1.10",
36
+ "fs-extra": "^11.3.0",
37
+ "inquirer": "^8.2.6"
38
+ },
39
+ "devDependencies": {
40
+ "@types/ejs": "^3.1.5",
41
+ "@types/fs-extra": "^11.0.4",
42
+ "@types/inquirer": "^8.2.6",
43
+ "@types/node": "^22.13.1",
44
+ "ts-node": "^10.9.2",
45
+ "typescript": "^5.7.3"
46
+ }
47
+ }