Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add info command and make some corrections #8

Merged
merged 8 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ package-lock.json

*.log
expressots.config.ts

*.tgz
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@expressots/cli",
"version": "1.2.2",
"version": "1.3.0-dev",
"description": "Expressots CLI - modern, fast, lightweight nodejs web framework (@cli)",
"author": "Richard Zampieri",
"license": "MIT",
Expand All @@ -14,8 +14,8 @@
"node": ">=18.10.0"
},
"funding": {
"type": "",
"url": ""
"type": "github",
"url": "https://github.com/sponsors/expressots"
},
"repository": {
"type": "git",
Expand Down
25 changes: 21 additions & 4 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { generateProject } from "./generate";
import { infoProject } from "./info";
import { createProject } from "./new";

export const CLI_VERSION = "1.2.2";

console.log(`\n[🐎 Expressots]\n`);

yargs(hideBin(process.argv))
.example("$0 new <project-name>", "Create a new Expresso TS project")
.scriptName("expressots")
.command(createProject())
.command(generateProject())
.command(infoProject())
.example("$0 new expressots-demo", "Create interactively")
.example("$0 new expressots-demo -d ./", "Create interactively with path")
.example("$0 new expressots-demo -p yarn -t opinionated", "Create silently")
.example("$0 new expressots-demo -p yarn -t opinionated -d ./", "Create silently with path")
.example("$0 generate service user-create", "Scaffold a service")
.example("$0 info", "Show CLI details")
.demandCommand(1, "You need at least one command before moving on")
.epilog("For more information, visit https://expresso-ts.com")
.help()
.parse();
.epilog("For more information: \n" +
"🌐 visit:\t https://expresso-ts.com\n" +
"💖 Sponsor:\t https://github.com/sponsors/expressots")
.help("help", "Show command help")
.alias("h", "help")
.version(false)
.wrap(140)
.parse();
2 changes: 1 addition & 1 deletion src/generate/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type CommandModuleArgs = {};
const generateProject = (): CommandModule<CommandModuleArgs, any> => {
return {
command: "generate [schematic] [path]",
describe: "Generate a schematic",
describe: "Scaffold a new resource",
aliases: ["g"],
builder: (yargs: Argv): Argv => {
yargs.positional("schematic", {
Expand Down
18 changes: 18 additions & 0 deletions src/info/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CommandModule } from "yargs";
import { infoForm } from "./form";

// eslint-disable-next-line @typescript-eslint/ban-types
type CommandModuleArgs = {};

const infoProject = (): CommandModule<CommandModuleArgs, any> => {
return {
command: "info",
describe: "Displays project details",
aliases: ["i"],
handler: async () => {
await infoForm();
},
};
};

export { infoProject };
39 changes: 39 additions & 0 deletions src/info/form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import chalk from "chalk";
import path from "path";
import fs from "fs";
import os from "os";
import { CLI_VERSION } from "../cli";
import { printError } from "../utils/cli-ui";

function getInfosFromPackage() {
try {
// Get the absolute path of the input directory parameter
const absDirPath = path.resolve();
// Load the package.json file
const packageJsonPath = path.join(absDirPath, "package.json");

const fileContents = fs.readFileSync(packageJsonPath, "utf-8");
const packageJson = JSON.parse(fileContents);

console.log(chalk.green("ExpressoTS Project:"));
console.log(chalk.bold(`\tName: ${packageJson.name}`));
console.log(chalk.bold(`\tDescription: ${packageJson.description}`));
console.log(chalk.bold(`\tVersion: ${packageJson.version}`));
console.log(chalk.bold(`\tAuthor: ${packageJson.author}`));
} catch (error) {
printError("No project information available.", "package.json not found!")
}
}

const infoForm = async (): Promise<void> => {
console.log(chalk.green("System informations:"));
console.log(chalk.bold(`\tOS Version: ${os.version()}`));
console.log(chalk.bold(`\tNodeJS version: ${process.version}`));

console.log(chalk.green("CLI Version:"));
console.log(chalk.bold(`\tCurrent version: v${CLI_VERSION}`));

getInfosFromPackage();
};

export { infoForm };
1 change: 1 addition & 0 deletions src/info/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./cli"
22 changes: 15 additions & 7 deletions src/new/cli.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { CommandModule, Argv } from "yargs";
import { Argv, CommandModule } from "yargs";
import { projectForm } from "./form";

type CommandModuleArgs = {};

const createProject = (): CommandModule<CommandModuleArgs, any> => {
return {
command: "new <project-name> [package-manager] [template]",
describe: "Create a new Expresso TS project",
command: "new <project-name> [package-manager] [template] [directory]",
describe: "Create a new project",
builder: (yargs: Argv): Argv => {
yargs
.positional("project-name", {
Expand All @@ -24,14 +24,22 @@ const createProject = (): CommandModule<CommandModuleArgs, any> => {
type: "string",
choices: ["npm", "yarn", "pnpm"],
alias: "p",
});
})
.option("directory", {
describe: "The directory for new project",
type: "string",
alias: "d",
})
.implies("package-manager", "template")
.implies("template", "package-manager")

return yargs;
},
handler: async ({projectName, packageManager, template}) => {
return await projectForm(projectName, packageManager, template);
handler: async ({projectName, packageManager , template, directory}) => {
return await projectForm(projectName, [packageManager, template, directory]);
},
};
};

export { createProject };

107 changes: 77 additions & 30 deletions src/new/form.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import inquirer from "inquirer";
import chalk from "chalk";
import degit from "degit";
import { spawn, execSync } from "child_process";
import { execSync, spawn } from "child_process";
import { Presets, SingleBar } from "cli-progress";
import degit from "degit";
import inquirer from "inquirer";
import fs from "node:fs";
import path from "node:path";
import { centerText } from "../utils/center-text";
import { printError } from "../utils/cli-ui";

async function packageManagerInstall({
packageManager,
Expand Down Expand Up @@ -35,7 +37,7 @@ async function packageManagerInstall({
if (code === 0) {
resolve("Installation Done!");
} else {
reject(new Error(`npm install exited with code ${code}`));
reject(new Error(`${packageManager} install exited with code ${code}`));
}
});
});
Expand All @@ -45,12 +47,12 @@ async function checkIfPackageManagerExists(packageManager: string) {
try {
execSync(`${packageManager} --version`);
return true;
} catch (_) {
throw new Error(`Package manager ${packageManager} is not installed`);
} catch (error) {
printError("Package manager not found!", packageManager);
process.exit(1);
}
}

// Change the package.json name to the user's project name
function changePackageName({
directory,
name,
Expand Down Expand Up @@ -79,13 +81,31 @@ enum Template {
}

const enum PackageManager {
npm,
yarn,
pnpm,
npm = "npm",
yarn = "yarn",
pnpm = "pnpm"
}

const projectForm = async (projectName: string, packageManager: PackageManager, template: keyof typeof Template): Promise<void> => {
const projectForm = async (projectName: string, args: any[]): Promise<void> => {
let answer: any;
const projName: string = projectName;
let packageManager: PackageManager | undefined;
let template: keyof typeof Template | undefined;
let directory: string | undefined;

// Resolving the argument order problem
for (const arg of args) {
if (args.length >= 3) {
if (arg === "npm" || arg === "yarn" || arg === "pnpm") {
packageManager = arg as PackageManager;
} else if (arg === "non-opinionated" || arg === "opinionated") {
template = arg as keyof typeof Template;
} else {
directory = arg;
}
}
}

if (packageManager && template) {
answer = {
name: projectName,
Expand All @@ -94,7 +114,6 @@ const projectForm = async (projectName: string, packageManager: PackageManager,
confirm: true,
};
} else {

answer = await inquirer.prompt([
{
type: "input",
Expand Down Expand Up @@ -128,19 +147,25 @@ const projectForm = async (projectName: string, packageManager: PackageManager,
},
]);
}

if (directory) {
if(!fs.existsSync(path.join(directory, answer.name))) {
answer.name = path.join(directory, answer.name);
} else {
printError("Directory already exists", directory);
process.exit(1);
}
}

// Hashmap of templates and their directories
const templates: Record<string, unknown> = {
"Non-Opinionated": "non_opinionated",
Opinionated: "opinionated",
};

if (answer.confirm) {
// Check if the package manager exists
await checkIfPackageManagerExists(answer.packageManager).catch((err) => {
console.log(chalk.red(err.message));
process.exit(1);
});

await checkIfPackageManagerExists(answer.packageManager);
console.log("\n");
const progressBar = new SingleBar(
{
format:
Expand All @@ -156,38 +181,60 @@ const projectForm = async (projectName: string, packageManager: PackageManager,

const [_, template] = answer.template.match(/(.*) ::/) as Array<string>;

const emitter = degit(
`expressots/expressots/templates/${templates[template]}`,
);

await emitter.clone(answer.name);

try {
const emitter = degit(
`expressots/expressots/templates/${templates[template]}`,
);

await emitter.clone(answer.name);
} catch (err: any) {
printError("Project already exists or Folder is not empty", answer.name);
process.exit(1);
}

progressBar.update(50, {
doing: "Installing dependencies",
});

// Run the package manager install in the directory
await packageManagerInstall({
packageManager: answer.packageManager,
directory: answer.name,
progressBar,
});


progressBar.update(90);

changePackageName({
directory: answer.name,
name: answer.name,
name: projName,
});

progressBar.update(100);

progressBar.stop();

console.log(chalk.green("Project created successfully!"));
console.log("Run the following commands to start the project:");
console.log(chalk.bold(`cd ${answer.name}`));
console.log(chalk.bold(`${answer.packageManager} start`));
console.log("\n");
console.log("🐎 Project ",chalk.green(projName), "created successfully!");
console.log("🤙 Run the following commands to start the project:\n");

console.log(chalk.bold.gray(`$ cd ${answer.name}`));
switch (answer.packageManager) {
case "npm":
console.log(chalk.bold.gray("$ npm run dev"));
break;
case "yarn":
console.log(chalk.bold.gray("$ yarn dev"));
break;
case "pnpm":
console.log(chalk.bold.gray("$ pnpm run dev"));
break;
}

console.log("\n");
console.log(chalk.bold.green(centerText("Happy coding!")));
console.log(chalk.bold.gray(centerText("Please consider donating to support the project.\n")));
console.log(chalk.bold.white(centerText("💖 Sponsor: https://github.com/sponsors/expressots")));
}
};

Expand Down
9 changes: 9 additions & 0 deletions src/utils/center-text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function centerText(text: string): string {
const terminalWidth = process.stdout.columns;
const padding = Math.floor((terminalWidth - text.length) / 2);
const centeredText = ' '.repeat(padding) + text;

return centeredText;
}

export { centerText };
5 changes: 5 additions & 0 deletions src/utils/cli-ui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import chalk from "chalk";

export function printError(message: string, component: string): void {
console.error(chalk.red(`\n\n😞 ${message}:`,chalk.white(`[${component}]`)));
}
Loading