Build Tools & Toolchain

45 min read Intermediate

Master modern JavaScript development tools including npm, bundlers, transpilers, and development workflows.

npm & Package Management

npm is the standard package manager for JavaScript projects.

Bash
# Initialize a new project
npm init -y

# Install dependencies
npm install lodash           # Production dependency
npm install -D jest          # Dev dependency (-D or --save-dev)
npm install -g typescript    # Global install

# Install from package.json
npm install                  # or npm i

# Update packages
npm update                   # Update all
npm update lodash            # Update specific package

# Check for outdated
npm outdated

# Remove package
npm uninstall lodash

# Run scripts
npm run build
npm run test
npm start                    # Special: no 'run' needed

# Audit for vulnerabilities
npm audit
npm audit fix
JSON
// package.json
{
    "name": "my-project",
    "version": "1.0.0",
    "description": "My JavaScript project",
    "main": "src/index.js",
    "type": "module",
    "scripts": {
        "start": "node src/index.js",
        "dev": "vite",
        "build": "vite build",
        "test": "jest",
        "lint": "eslint src",
        "format": "prettier --write src"
    },
    "dependencies": {
        "lodash": "^4.17.21"
    },
    "devDependencies": {
        "jest": "^29.0.0",
        "eslint": "^8.0.0",
        "prettier": "^3.0.0",
        "vite": "^5.0.0"
    },
    "engines": {
        "node": ">=18.0.0"
    }
}
Version Ranges
  • ^4.17.21 - Compatible with 4.x.x (most common)
  • ~4.17.21 - Patch updates only (4.17.x)
  • 4.17.21 - Exact version
  • * - Any version (avoid in production)

Bundlers

Bundlers combine modules into optimized files for production.

JavaScript
// ===== Vite (Recommended for new projects) =====
// Fast dev server with HMR, optimized builds

// vite.config.js
import { defineConfig } from "vite";

export default defineConfig({
    root: "src",
    build: {
        outDir: "../dist",
        minify: "terser",
        sourcemap: true
    },
    server: {
        port: 3000,
        open: true
    },
    resolve: {
        alias: {
            "@": "/src"
        }
    }
});


// ===== Webpack =====
// Most configurable, large ecosystem

// webpack.config.js
const path = require("path");

module.exports = {
    mode: "production",
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name].[contenthash].js",
        clean: true
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: "babel-loader"
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    },
    optimization: {
        splitChunks: {
            chunks: "all"
        }
    }
};


// ===== esbuild =====
// Extremely fast, written in Go

// esbuild.config.js
require("esbuild").build({
    entryPoints: ["src/index.js"],
    bundle: true,
    minify: true,
    sourcemap: true,
    outfile: "dist/bundle.js"
});

Transpilers

Convert modern JavaScript/TypeScript to widely supported versions.

JavaScript
// ===== Babel =====
// Transform modern JS to older versions

// babel.config.json
{
    "presets": [
        ["@babel/preset-env", {
            "targets": "> 0.5%, not dead",
            "useBuiltIns": "usage",
            "corejs": 3
        }]
    ],
    "plugins": [
        "@babel/plugin-transform-runtime"
    ]
}

// Input (ES2020+):
const result = array.at(-1);
const value = obj?.property ?? "default";

// Output (ES5):
var result = array[array.length - 1];
var value = obj !== null && obj !== void 0 
    ? obj.property 
    : "default";


// ===== TypeScript =====
// Type-safe JavaScript superset

// tsconfig.json
{
    "compilerOptions": {
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "bundler",
        "strict": true,
        "noEmit": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true,
        "declaration": true,
        "outDir": "./dist",
        "rootDir": "./src",
        "baseUrl": ".",
        "paths": {
            "@/*": ["./src/*"]
        }
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}

Linters & Formatters

Ensure code quality and consistent style.

JavaScript
// ===== ESLint =====
// Find and fix code problems

// eslint.config.js (Flat config - ESLint 9+)
import js from "@eslint/js";

export default [
    js.configs.recommended,
    {
        rules: {
            "no-unused-vars": "warn",
            "no-console": "warn",
            "eqeqeq": "error",
            "curly": "error",
            "prefer-const": "error",
            "no-var": "error"
        },
        languageOptions: {
            ecmaVersion: 2024,
            sourceType: "module"
        }
    }
];

// .eslintignore
// node_modules
// dist
// coverage


// ===== Prettier =====
// Opinionated code formatter

// .prettierrc
{
    "semi": true,
    "singleQuote": false,
    "tabWidth": 4,
    "trailingComma": "es5",
    "printWidth": 80,
    "bracketSpacing": true,
    "arrowParens": "always"
}

// Run with:
// npx prettier --write src


// ===== Husky + lint-staged =====
// Run linters on git commit

// package.json
{
    "scripts": {
        "prepare": "husky install"
    },
    "lint-staged": {
        "*.js": ["eslint --fix", "prettier --write"],
        "*.{json,md}": "prettier --write"
    }
}

// .husky/pre-commit
// npx lint-staged

Task Runners & Scripts

Automate development tasks with npm scripts.

JSON
// package.json scripts
{
    "scripts": {
        // Development
        "dev": "vite",
        "dev:host": "vite --host",
        
        // Building
        "build": "tsc && vite build",
        "build:watch": "tsc --watch",
        
        // Testing
        "test": "jest",
        "test:watch": "jest --watch",
        "test:coverage": "jest --coverage",
        
        // Linting & Formatting
        "lint": "eslint src --ext .js,.ts",
        "lint:fix": "eslint src --ext .js,.ts --fix",
        "format": "prettier --write \"src/**/*.{js,ts,json}\"",
        "format:check": "prettier --check \"src/**/*.{js,ts,json}\"",
        
        // Type checking
        "typecheck": "tsc --noEmit",
        
        // Combined
        "validate": "npm run lint && npm run typecheck && npm run test",
        
        // Pre-commit hooks
        "prepare": "husky install",
        
        // Deployment
        "preview": "vite preview",
        "deploy": "npm run build && npm run upload"
    }
}
Bash
# Run multiple scripts in sequence
npm run lint && npm run test

# Run scripts in parallel (use npm-run-all)
npm install -D npm-run-all

# package.json:
# "dev": "run-p dev:*",
# "dev:server": "vite",
# "dev:types": "tsc --watch"

# Pass arguments to scripts
npm run test -- --watch

# Environment variables
cross-env NODE_ENV=production npm run build

Project Structure

Organize files for maintainability and scalability.

Plain Text
my-project/
├── src/
│   ├── index.js          # Entry point
│   ├── components/       # UI components
│   │   ├── Button.js
│   │   └── Modal.js
│   ├── services/         # API/business logic
│   │   ├── api.js
│   │   └── auth.js
│   ├── utils/            # Helper functions
│   │   ├── format.js
│   │   └── validation.js
│   ├── styles/           # CSS/SCSS files
│   │   └── main.css
│   └── assets/           # Images, fonts
│       └── logo.svg
├── tests/                # Test files
│   ├── unit/
│   └── integration/
├── public/               # Static files
│   └── index.html
├── dist/                 # Build output (gitignored)
├── node_modules/         # Dependencies (gitignored)
├── .gitignore
├── .eslintrc.js
├── .prettierrc
├── babel.config.js
├── vite.config.js
├── package.json
├── package-lock.json
└── README.md

esbuild - Ultra-Fast Bundler

esbuild is an extremely fast JavaScript bundler written in Go. It's 10-100x faster than traditional bundlers.

JavaScript - esbuild Configuration
// build.mjs - esbuild script
import * as esbuild from 'esbuild';

// Simple build
await esbuild.build({
    entryPoints: ['src/index.js'],
    bundle: true,
    outfile: 'dist/bundle.js',
    minify: true,
    sourcemap: true
});

// Production configuration
const config = {
    entryPoints: ['src/index.js'],
    bundle: true,
    outdir: 'dist',
    minify: true,
    sourcemap: true,
    splitting: true,           // Code splitting
    format: 'esm',             // ES modules output
    target: ['es2020'],        // Target environment
    define: {
        'process.env.NODE_ENV': '"production"'
    },
    loader: {
        '.png': 'file',
        '.svg': 'text',
        '.css': 'css'
    }
};

// Build with watch mode
const ctx = await esbuild.context(config);
await ctx.watch();
console.log('Watching for changes...');

// Development server
const ctx2 = await esbuild.context({
    ...config,
    minify: false
});

await ctx2.serve({
    servedir: 'dist',
    port: 3000
});

// Using as a library
const result = await esbuild.build({
    entryPoints: ['src/index.js'],
    bundle: true,
    write: false  // Get output in memory
});

console.log(result.outputFiles[0].text);

CI/CD Pipelines

Continuous Integration and Continuous Deployment automate testing, building, and deploying your applications.

YAML - GitHub Actions Workflow
# .github/workflows/ci.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [18.x, 20.x]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run tests
        run: npm test -- --coverage
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3

  build:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20.x
          cache: 'npm'
      
      - run: npm ci
      - run: npm run build
      
      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: build
          path: dist/
      
      - name: Deploy to Netlify
        uses: netlify/actions/cli@master
        with:
          args: deploy --prod --dir=dist
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

Deployment Strategies

Deploy your JavaScript applications to various hosting platforms with optimized configurations.

JavaScript - Deployment Configurations
// vite.config.js - Production build
export default {
    base: '/my-app/',  // For GitHub Pages subdirectory
    build: {
        outDir: 'dist',
        assetsDir: 'assets',
        sourcemap: false,
        rollupOptions: {
            output: {
                manualChunks: {
                    vendor: ['react', 'react-dom'],
                    utils: ['lodash', 'date-fns']
                }
            }
        }
    }
};

// vercel.json - Vercel configuration
/*
{
    "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
    "headers": [
        {
            "source": "/assets/(.*)",
            "headers": [
                { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
            ]
        }
    ]
}
*/

// netlify.toml - Netlify configuration
/*
[build]
  command = "npm run build"
  publish = "dist"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

[[headers]]
  for = "/assets/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"
*/

// Environment variables management
// .env.production
/*
VITE_API_URL=https://api.production.com
VITE_ANALYTICS_ID=UA-123456-1
*/

// Access in code
const apiUrl = import.meta.env.VITE_API_URL;

// Docker deployment
/*
# Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
*/
Build Tool Selection Guide
  • Vite: Best for new projects, modern frameworks, fast dev server
  • esbuild: Best for simple builds, maximum speed, libraries
  • Webpack: Best for complex configurations, legacy projects
  • Rollup: Best for libraries, ES modules, tree shaking

Summary

npm

Package management, scripts

Vite/Webpack

Bundle and optimize code

Babel/TypeScript

Transpile modern JS/TS

ESLint

Find and fix code issues

Prettier

Consistent code formatting

Husky

Git hooks for automation