How to Initialize and Structure a Node.js CLI Project with TypeScript

This guide walks you through creating a demo-cli project, configuring package.json, setting up bin scripts, understanding Linux and Node bin execution, choosing global versus local installation, adding TypeScript, ESLint, Prettier, lint‑staged, and finally publishing the package to npm.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How to Initialize and Structure a Node.js CLI Project with TypeScript

1. Initialize Project

Create a project folder (e.g., demo-cli) and run the following command to initialize the project: npm init Follow the prompts to fill in basic package.json information.

1.1 Configure package.json

Open the project in VSCode and edit the important fields in package.json:

name : the package name used when others import it.

version : follows major.minor.patch semantics for upgrades.

main : entry file path for module resolution.

scripts : custom npm scripts.

When you run npm run , npm creates a new shell, adds node_modules/.bin to PATH , executes the script, then restores PATH .

1.1.5 bin

Map custom command names to executable files:

{
  ...
  "bin": {
    "demo-cli": "bin/demo-cli"
  },
  ...
}

If the command name matches the package name, you can shorten it:

{
  ...
  "bin": "bin/demo-cli",
  ...
}

1.2 How bin Commands Run

1.2.1 Role of Linux bin directories

Bash first checks if a command contains a slash; if not, it searches functions, built‑ins, then the directories listed in PATH . The type command helps locate the executable.

Common bin directories include /bin, /sbin, /usr/bin, /usr/local/bin, etc.

1.2.2 Node bin

Find the Node installation path with:

echo $PATH

The directory /Users/hopewlliu/.nvm/versions/node/v14.17.3/bin contains all global Node commands.

To create a symlink for a custom command:

ln -s ../lib/node_modules/@tencent/imserver-cli/bin/imserver ./imserver2

Remove the symlink with:

rm ./imserver2

1.2.3 Global vs. Local Installation

1.2.3.1 Global installation

Installing with -g places the package under ~/.nvm/versions/node/.../lib/node_modules. If the package defines a bin field, npm creates a symlink in ~/.nvm/versions/node/.../bin.

1.2.3.2 Local installation

Local packages reside in the project’s node_modules. Custom commands are linked in node_modules/.bin and can be run via npm scripts, which temporarily add node_modules/.bin to PATH.

1.2.4 Execution principle of target files

CLI entry files should start with the shebang line: #!/usr/bin/env node This tells the system to use the node interpreter found in PATH. The file can be executed directly without specifying the interpreter.

2. Directory Structure

.
├── README.md
├── bin
│   └── demo-cli
├── dist
├── lib
├── node_modules   // dependencies
├── package-lock.json
└── package.json

2.1 README.md

Project introduction, usage instructions, options, etc.

2.2 bin

Stores executable files for custom commands.

2.3 dist

Compiled output for publishing.

2.4 lib

Source code. Ensure the main field in package.json points to the compiled entry.

3. Additional Configuration

3.1 TypeScript Support

Install development dependencies:

npm install --save-dev typescript @types/node rimraf

Create tsconfig.json (excerpt):

{
  "compilerOptions": {
    "baseUrl": ".",
    "rootDir": "lib",
    "lib": ["esnext"],
    "module": "commonjs",
    "outDir": "dist/lib",
    "allowJs": true,
    "strict": true,
    "declaration": true,
    "target": "es6",
    "suppressImplicitAnyIndexErrors": true
  },
  "include": ["lib"]
}

Add npm scripts for cleaning, development, and pre‑publish:

{
  "scripts": {
    "clean": "rimraf dist",
    "dev": "npm run clean && tsc -w",
    "prepublish": "npm run clean && tsc"
  }
}

3.2 ESLint & Prettier

Install:

npm i -D [email protected] @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier eslint-plugin-prettier prettier

Configure .eslintrc.js:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "plugins": ["prettier", "@typescript-eslint"],
  "extends": ["prettier"],
  "rules": {
    "no-var": "error",
    "prettier/prettier": "error"
  }
}

Ignore generated files with .eslintignore and .prettierignore (both contain dist and node_modules).

Prettier configuration ( .prettierrc):

{
  "useTabs": false,
  "printWidth": 120,
  "singleQuote": true,
  "trailingComma": "es5",
  "arrowParens": "always"
}

3.3 Pre‑commit Linting

Install husky and lint‑staged: npm install -D husky lint-staged Add to package.json:

{
  "lint-staged": {
    "*.{js,ts}": ["prettier-eslint --write", "eslint --fix", "git add"]
  }
}

Initialize husky and add a pre‑commit hook:

npx husky install
npx husky add .husky/pre-commit "echo \"git commit trigger husky pre-commit hook\" && npx lint-staged"

3.4 .gitignore & .npmignore

Typical entries:

node_modules
package-lock.json
dist

.npmignore should also exclude source files and config files that are not needed in the published package.

4. Common CLI Libraries

commander – command and argument parsing

glob – file traversal

shelljs – shell command utilities

prompts / inquirer – interactive console input

fs-extra – file system operations

chalk – colored logging

debug – debugging utilities

execa – execute shell commands

5. Publishing to npm

First login or register: npm adduser // or npm login Then publish (prepublish script ensures the build runs first):

npm publish

6. Summary

Creating a CLI demo involves many steps, especially configuring ESLint in VSCode. Understanding Linux command lookup and Node bin execution is crucial for CLI development, and the same principles can be extended to other languages such as C++ extensions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CLITypeScriptNode.jsnpmpackage managementESLintPrettier
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.