Library vs CLI Packages: Local and Global Installation Guide and Usage Patterns
Overview
When working with Node.js and npm, you'll encounter two primary types of packages: library packages and CLI (Command-Line Interface) packages. Understanding the distinction between these package types is essential for effective development and proper dependency management.
What are Library Packages?
Library packages are modules that you import and use directly within your application code. They provide reusable functions, classes, and utilities that enhance your application's functionality.
Common examples include:
lodash
- Utility functions for working with arrays, objects, and moreaxios
- HTTP client for making API requestsexpress
- Web application frameworkreact
- UI library for building user interfaces
Library packages are typically installed locally as project dependencies and imported into your code:
import express from "express";
import axios from "axios";
What are CLI Packages?
CLI packages are tools that run directly from your terminal or command line. They provide executable commands that perform specific tasks, often related to development workflows, project scaffolding, or build processes.
Common examples include:
vite
- Frontend build tool and development servercreate-react-app
- React project generatoreslint
- Code linting tooltypescript
- TypeScript compiler
CLI packages are distinguished by their executable files, which are referenced in the package's package.json
under the bin
field. When installed, npm creates symbolic links or wrapper scripts in node_modules/.bin/
(for local installations) or in a system-wide bin directory (for global installations). These executables typically begin with a shebang line (#!/usr/bin/env node
) that instructs the operating system to execute the file using Node.js.
How CLI Packages Work
Example of a CLI package configuration: CLI packages include executable files that allow them to run as terminal commands. Understanding how these executables are created and invoked is essential for working with CLI tools effectively.
Anatomy of a CLI Package
Unlike library packages that export modules for import, CLI packages define executable entry points through their package.json
configuration:
{
"name": "my-cli-tool",
"version": "1.0.0",
"bin": {
"my-tool": "./bin/cli.js"
}
}
The bin
field maps command names to executable file paths. In this example, the package name is my-cli-tool
, but the actual command that users run is my-tool
(defined in the bin
field). This allows package authors to create user-friendly command names that differ from the npm package name.
The corresponding executable file (bin/cli.js
) includes a shebang directive and the command logic:
#!/usr/bin/env node
console.log("Hello from my CLI tool!");
// Your CLI logic here
Key components:
- Shebang line (
#!/usr/bin/env node
): Tells the system to execute this file using Node.js - bin field: Maps command name(s) to executable file path(s)
- Executable permissions: The file must be executable (npm handles this during installation)
The Executable File
When you install a CLI package, npm automatically sets up these executables so they can be invoked from the command line. This cross-platform mechanism allows JavaScript-based tools to function as native commands without requiring separate shell scripts for different operating systems.
CLI Packages in Action
If you've worked with React, you've likely used Vite to scaffold a new project:
npm create vite@latest
This command uses Vite's CLI to interactively set up a new React (or Vue, Svelte, etc.) project with the necessary configuration and file structure.
Key Differences
Aspect | Library Packages | CLI Packages |
---|---|---|
Usage | Imported in application code | Executed from terminal |
Purpose | Provide functionality to your app | Perform development tasks |
Installation | Usually local (npm install ) | Can be local or global |
Access | Via import or require | Via command line |
Packages That Are Both Library and CLI
Many packages serve dual purposes, functioning both as importable libraries and command-line tools. These hybrid packages offer maximum flexibility in how you integrate them into your workflow.
Installation Note: When installing CLI packages locally, they're typically added as development dependencies since they're used during development rather than in production:
npm install -D typescript eslint prettier
# or
npm install --save-dev typescript eslint prettier
Common examples:
TypeScript
- As CLI:
tsc app.ts
- Compile TypeScript files - As Library: Import TypeScript compiler API in your build tools
// As library
import * as ts from "typescript";
const result = ts.transpile('let x: string = "hello"');
// As CLI
// tsc --init
// tsc src/app.ts
ESLint
- As CLI:
eslint src/
- Lint your code from terminal - As Library: Integrate ESLint into build processes or editors
// As library
import { ESLint } from "eslint";
const eslint = new ESLint();
const results = await eslint.lintFiles(["src/**/*.js"]);
// As CLI
// eslint . --fix
// eslint src/ --ext .js,.ts
Prettier
- As CLI:
prettier --write .
- Format code from command line - As Library: Integrate formatting into your applications
// As library
import prettier from "prettier";
const formatted = prettier.format("const x={a:1}", { parser: "babel" });
// As CLI
// prettier --write src/
// prettier --check .
Webpack
- As CLI:
webpack
- Bundle your application - As Library: Use webpack API in custom build scripts
// As library
import webpack from "webpack";
import config from "./webpack.config.js";
webpack(config, (err, stats) => {
// Handle build results
});
// As CLI
// webpack --mode production
// webpack serve --mode development
Why this matters:
- Flexibility: Use the same tool in different contexts
- Integration: Embed functionality directly into your applications
- Automation: Programmatically control tools in build scripts
- Consistency: Same underlying logic whether used as library or CLI
Local vs Global Installation
Understanding where to install packages is just as important as knowing what type they are.
Local Installation (Recommended)
Installing packages locally means they're added to your project's node_modules
folder and listed in package.json
.
npm install <package-name>
Where local packages are stored:
Library packages are stored in node_modules/<package-name>/
. For example:
- Installing
lodash
createsnode_modules/lodash/
- Installing
axios
createsnode_modules/axios/
- Installing
react
createsnode_modules/react/
Each package folder contains:
package.json
- Package metadata and dependenciesindex.js
or main entry file - The code you importnode_modules/
- The package's own dependencies (nested)
CLI packages have their executable files stored in node_modules/.bin/
. For example:
- Installing
vite
locally createsnode_modules/.bin/vite
- Installing
eslint
locally createsnode_modules/.bin/eslint
Running locally installed packages:
# Library packages - import in your code
import axios from 'axios'; // Node.js finds it in node_modules/axios/
# CLI packages - multiple ways to run
npx vite # Using npx (recommended)
./node_modules/.bin/vite # Direct path reference
npm run dev # Via npm scripts where "dev": "vite"
Benefits:
- Different projects can use different versions
- Dependencies are tracked in
package.json
- Team members get consistent versions
- No version conflicts between projects
- CLI tools are isolated per project
Global Installation
Global packages are installed system-wide and available from any directory.
npm install -g <package-name>
When to use global installation:
- CLI tools you use across multiple projects
- Development utilities not tied to specific projects
- Tools you want available system-wide
Note: Modern best practices often favor using npx
to run CLI tools without global installation, ensuring you always use the correct version for each project.
Where global packages are stored:
Global packages are installed in a system-wide directory, separate from your project folders. The exact location varies by operating system and Node.js installation method:
- Windows:
C:\Users\<YourUsername>\AppData\Roaming\npm\node_modules
- macOS/Linux:
/usr/local/lib/node_modules
or~/.npm-global/node_modules
To find your global package installation directory, use:
npm root -g
This command displays the exact path where npm installs global packages on your system.
Example:
npm install -g typescript
# TypeScript is now available globally
tsc --version
# Check where it was installed
npm root -g
# Output: /usr/local/lib/node_modules (or your system's global path)
Global CLI executables are placed in a directory that's added to your system's PATH, making them accessible from any terminal location. Common locations include:
- Windows:
C:\Users\<YourUsername>\AppData\Roaming\npm
- macOS/Linux:
/usr/local/bin
Best Practices
- Install libraries locally - Always add application dependencies to your project
- Use npx for CLI tools - Run CLI packages without global installation:
npx vite
- Document in package.json - Ensure all dependencies are tracked
- Avoid global installs when possible - They can cause version conflicts between projects
Understanding these concepts will help you manage dependencies more effectively and maintain cleaner, more reproducible development environments.