@avleon/cli
Version:
> **🚧 This project is in active development.** > > It is **not stable** and **not ready** for live environments. > Use **only for testing, experimentation, or internal evaluation**. > > ####❗ Risks of using this in production: > > - 🔄 Breaking
319 lines (301 loc) • 10.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createApplication = createApplication;
const fs_extra_1 = __importDefault(require("fs-extra"));
const paths_1 = require("./paths");
const prettier = __importStar(require("prettier"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const inquirer_1 = __importDefault(require("inquirer"));
const child_process_1 = require("child_process");
const util_1 = __importDefault(require("util"));
const lodash_1 = require("lodash");
const execAsync = util_1.default.promisify(child_process_1.exec);
const getShell = () => {
if (os_1.default.platform() === 'win32') {
return process.env.ComSpec || 'cmd.exe'; // Windows fallback
}
return process.env.SHELL || '/bin/sh'; // macOS/Linux fallback
};
async function askForPackageManager() {
const { packageManager } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'packageManager',
message: 'Which package manager do you want to use?',
choices: ['npm', 'yarn', 'pnpm'],
default: 'npm'
}
]);
return packageManager;
}
async function installDependencies(pm, fname) {
return new Promise((resolve, reject) => {
const child = (0, child_process_1.spawn)(pm, ['install'], {
cwd: fname,
shell: getShell(),
stdio: 'inherit',
});
child.on('exit', (code) => {
if (code === 0) {
console.log('✅ Dependencies installed successfully.');
console.log(`cd ${fname}`);
console.log(pm == 'npm' ? `npm run start:dev` : `${pm} start:dev`);
resolve();
}
else {
console.error(`❌ Installation failed with exit code ${code}`);
reject(new Error(`Install failed with code ${code}`));
}
});
child.on('error', (err) => {
console.error('❌ Failed to start install process:', err);
reject(err);
});
});
}
async function createApplication(fname, flags = {}) {
let appPaths = (0, paths_1.appPaths)(fname);
const pm = await askForPackageManager();
// const dirs = Object.values(appPaths);
// for (let i = 0; i < dirs.length; i++) {
// await fs.ensureDir(dirs[i]);
// }
await fs_extra_1.default.ensureDir(appPaths.root + "/src");
await fs_extra_1.default.ensureDir(appPaths.root + "/src/controllers");
await fs_extra_1.default.ensureDir(appPaths.root + "/src/config");
await fs_extra_1.default.ensureDir(appPaths.root + "/src/services");
await fs_extra_1.default.ensureDir(appPaths.root + "/src/models");
await fs_extra_1.default.ensureDir(appPaths.root + "/test");
await fs_extra_1.default.ensureDir(appPaths.root + "/public");
// copy
// home controller
const homeService = `
import { AppService } from '@avleon/core';
@AppService
export class HomeService{
sayHello(){
return "Hello World!";
}
}
`;
const formatHomeService = await prettier.format(homeService, {
singleQuote: true,
singleAttributePerLine: true,
parser: "typescript",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.services, "home.service.ts"), formatHomeService);
// home controller
const homeContrlelr = `
import { ApiController, Get } from '@avleon/core';
import { HomeService } from '../services/home.service';
@ApiController
export class HomeController{
constructor(
private readonly homeService: HomeService
){}
@Get()
sayHello(){
return this.homeService.sayHello();
}
}
`;
const formatHomeController = await prettier.format(homeContrlelr, {
singleQuote: true,
singleAttributePerLine: true,
parser: "typescript",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.controllers, "home.controller.ts"), formatHomeController);
// openapi config file
const openapiConfigFile = `
import { AppConfig, Environment, IConfig } from "@avleon/core";
@AppConfig
export class OpenApiConfig implements IConfig {
config(env: Environment) {
return {
info: {
title: '${(0, lodash_1.capitalize)(fname)}',
version: '0.0.1',
},
}
}
}
`;
const formatConfigFile = await prettier.format(openapiConfigFile, {
singleQuote: true,
singleAttributePerLine: true,
parser: "typescript",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.configs, "openapi.config.ts"), formatConfigFile);
// main file
const mainFunc = `
import { Avleon } from '@avleon/core';
import { OpenApiConfig } from './config/openapi.config';
import { HomeController } from './controllers/home.controller';
const app = Avleon.createApplication();
if(app.isDevelopemtn()){
app.useOpenApi(OpenApiConfig);
}
app.useControllers([ HomeController ]);
app.useStaticFiles();
export default app;
`;
const formatMainFunc = await prettier.format(mainFunc, {
singleQuote: true,
singleAttributePerLine: true,
parser: "typescript",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.src, "app.ts"), formatMainFunc);
// create serve.ts
const serveFunc = `
import app from "./app";
async function start(){
app.run(); // default run on :4000
}
start();
`;
const formatServeFunc = await prettier.format(serveFunc, {
singleQuote: true,
singleAttributePerLine: true,
parser: "typescript",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.src, "serve.ts"), formatServeFunc);
// create package.json
const packageJson = `
{
"name": "${(0, lodash_1.snakeCase)(fname)}",
"version": "0.0.1",
"description": "Basic avleon application",
"main": "dist/serve.js",
"scripts": {
"start:dev": "nodemon ./src/serve.ts",
"start:debug": "nodemon --watch ./src --ext ts --exec node --inspect=0.0.0.0:9229 -r ts-node/register ./src/serve.ts",
"clean": "rimraf ./dist",
"build": "npm run clean & tsc --project ./tsconfig.build.json",
"start": "node ./dist/serve.js",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/e2e-spec.json"
},
"keywords": ["avleon","nodejs","typescript"],
"license": "ISC",
"dependencies": {
"@avleon/core": "^0.0.36",
"@avleon/cli": "^0.0.42",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"rimraf": "^6.0.1",
"reflect-metadata": "^0.2.2"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^24.0.3",
"jest": "^29.7.0",
"ts-jest": "^29.2.6",
"nodemon": "^3.1.7",
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
},
"jest": {
"preset": "ts-jest",
"rootDir": "src",
"testRegex": ".*/\/\.spec/\/\.ts$",
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
`;
const formattedPackageJson = await prettier.format(packageJson, {
parser: "json",
});
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.root, "package.json"), formattedPackageJson);
const tsconfigJson = await prettier.format(`{
"compilerOptions": {
"target": "ES2017",
"module": "CommonJS",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "./dist",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}`, { parser: "json" });
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.root, "tsconfig.json"), tsconfigJson);
const tsconfigBuildJson = await prettier.format(`{
"extends": "./tsconfig.json",
"exclude": ["node_modules","coverage", "test", "dist", "**/*spec.ts"]
}
`, { parser: "json" });
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.root, "tsconfig.build.json"), tsconfigBuildJson);
// write e2e-test.json
const e2eJson = await prettier.format(`{
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+/\/\.(t|j)s$": "ts-jest"
}
}`, { parser: "json" });
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.root, "test/e2e-spec.json"), e2eJson);
await fs_extra_1.default.writeFile(path_1.default.join(appPaths.root, ".gitignore"), `node_modules/\ndist/\n.env`);
try {
console.log(`Running ${pm} install...`);
await execAsync(`cd ${fname}`);
await installDependencies(pm, fname);
}
catch (error) {
console.error('❌ Installation failed:', error);
}
console.info("Enjoy :D");
}