<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nasouh Mrstani</title>
    <description>The latest articles on DEV Community by Nasouh Mrstani (@nasouhmr).</description>
    <link>https://dev.to/nasouhmr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F305604%2Fb7b567b3-8ecd-45a4-a6f0-e2b0ceecdc78.jpeg</url>
      <title>DEV Community: Nasouh Mrstani</title>
      <link>https://dev.to/nasouhmr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nasouhmr"/>
    <language>en</language>
    <item>
      <title>🚀 Build Your Own App Launcher CLI with Node.js</title>
      <dc:creator>Nasouh Mrstani</dc:creator>
      <pubDate>Fri, 22 Aug 2025 16:13:51 +0000</pubDate>
      <link>https://dev.to/nasouhmr/build-your-own-app-launcher-cli-with-nodejs-290n</link>
      <guid>https://dev.to/nasouhmr/build-your-own-app-launcher-cli-with-nodejs-290n</guid>
      <description>&lt;p&gt;Managing multiple projects on your machine can get messy—especially if you’re constantly jumping between NestJS, Angular, and other apps. Wouldn’t it be great to have a command-line tool that lists all your projects, lets you choose one, and runs your desired command with a single keystroke?&lt;/p&gt;

&lt;p&gt;That’s exactly what I built: a custom App Launcher CLI using Node.js. In this tutorial, I’ll show you how to make one yourself.&lt;/p&gt;

&lt;p&gt;✨ &lt;strong&gt;Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;📂 Automatically scans a folder for apps/projects.&lt;br&gt;
📌 Sorts apps by last modified for quick access.&lt;br&gt;
⚡ Run custom commands like npm run start:dev or ng serve --open.&lt;br&gt;
🛠 Commands are fully configurable in .env.&lt;br&gt;
🖥 Optional: Convert it into a desktop EXE app with pkg.&lt;/p&gt;

&lt;p&gt;🛑 &lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Node.js installed&lt;br&gt;
Basic knowledge of running CLI commands&lt;/p&gt;

&lt;p&gt;📂 &lt;strong&gt;Project Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a new folder and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir app-launcher
cd app-launcher
npm init -y
npm install inquirer dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚙️ Add a .env File&lt;/p&gt;

&lt;p&gt;Define your base path for apps and the commands you want available:&lt;br&gt;
APPS_BASE_PATH=D:/apps&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COMMAND_1_NAME=NestJS (Start Dev)
COMMAND_1_CMD=npm run start:dev

COMMAND_2_NAME=Angular (Serve with Open)
COMMAND_2_CMD=ng serve --open

COMMAND_3_NAME=Open Shell
COMMAND_3_CMD=cmd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 The App Launcher Code&lt;br&gt;
Here’s the main script app-launcher.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const cp = require("child_process");
const fs = require("fs");
const path = require("path");
const inquirer = require("inquirer");
require("dotenv").config();

const style = {
  reset: "\x1b[0m",
  bright: "\x1b[1m",
  fgGreen: "\x1b[32m",
  fgCyan: "\x1b[36m",
  fgYellow: "\x1b[33m",
  fgMagenta: "\x1b[35m"
};

const APP_NAME = "App Launcher";
const APP_VERSION = "1.0.0";
const APP_AUTHOR = "Nasouh Mrstani / Senior Web Developer";
const APP_EMAIL = "nasouhmra@gmail.com";
const baseDir = process.env.APPS_BASE_PATH || "D:/apps";

function boxText(text) {
  const line = "─".repeat(text.length + 2);
  return [
    style.fgCyan + `┌${line}┐` + style.reset,
    style.fgCyan + ` ${text} ` + style.reset,
    style.fgCyan + `└${line}┘` + style.reset,
  ].join("\n");
}

function showWelcome() {
  const line = style.fgGreen + "═".repeat(50) + style.reset;
  console.log(line);
  console.log(boxText(style.bright + APP_NAME + "  " + APP_VERSION + style.reset));
  console.log(style.fgMagenta + `Author: ${APP_AUTHOR}` + style.reset);
  console.log(style.fgCyan + `Email: ${APP_EMAIL}` + style.reset);
  console.log(style.fgGreen + `Apps path: ${baseDir}` + style.reset);
  console.log(line + "\n");
}

const COMMANDS = [];
for (let i = 1; ; i++) {
  const name = process.env[`COMMAND_${i}_NAME`];
  const cmd = process.env[`COMMAND_${i}_CMD`];
  if (!name || !cmd) break;
  const parts = cmd.split(" ");
  COMMANDS.push({ name, cmd: parts });
}

async function main() {
  showWelcome();

  const dirs = fs.readdirSync(baseDir, { withFileTypes: true })
    .filter(d =&amp;gt; d.isDirectory())
    .map(d =&amp;gt; {
      const folderPath = path.join(baseDir, d.name);
      const stats = fs.statSync(folderPath);
      return { name: d.name, value: folderPath, mtime: stats.mtime };
    });

  if (dirs.length === 0) {
    console.log(style.fgMagenta + "❌ No folders found in " + baseDir + style.reset);
    return;
  }

  dirs.sort((a, b) =&amp;gt; b.mtime - a.mtime);

  const { projectPath } = await inquirer.prompt([
    {
      type: "list",
      name: "projectPath",
      message: style.fgGreen + "Select a folder" + style.reset,
      pageSize: 15,
      choices: dirs.map(d =&amp;gt; ({
        name: style.fgCyan + d.name + style.reset,
        value: d.value
      })),
    }
  ]);

  console.log(boxText(path.basename(projectPath)));

  const { command } = await inquirer.prompt([
    {
      type: "list",
      name: "command",
      message: `Choose a command for "${path.basename(projectPath)}"`,
      choices: COMMANDS.map(c =&amp;gt; ({ name: c.name, value: c.cmd })),
    },
  ]);

  console.log(style.fgYellow + `Running: ${command.join(" ")} in ${projectPath}` + style.reset);

  cp.spawn(command[0], command.slice(1), {
    cwd: projectPath,
    shell: true,
    stdio: "inherit"
  });
}

main().catch(err =&amp;gt; console.error("Error:", err));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;▶️ &lt;strong&gt;Run the Launcher&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node app-launcher.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🖥 Convert to Desktop App (Optional)&lt;br&gt;
If you want a single .exe you can run from your desktop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g pkg
pkg app-launcher.js --targets node18-win-x64 --output app-launcher.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have a portable Windows executable 🎉.&lt;/p&gt;

&lt;p&gt;📬 &lt;strong&gt;Wrapping Up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With just a bit of Node.js and some CLI magic, you now have your own App Launcher that makes managing projects faster and easier.&lt;/p&gt;

&lt;p&gt;✅ Clean interface&lt;br&gt;
✅ Customizable with .env&lt;br&gt;
✅ Runs on Windows as EXE with pkg&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nasouhmr/app-launcher" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
