JavaScript Build Tools
Why Build Tools?
Modern JavaScript projects use TypeScript, JSX, Sass, ES modules, and npm packages. Browsers don’t natively understand all of this. Build tools transform, bundle, and optimize code for production deployment.
The Modern Toolchain
Source (TS/JSX/Sass)
→ Transpile (esbuild, Babel, tsc)
→ Bundle (Rollup, Webpack, esbuild)
→ Optimize (minify, tree-shake, code-split)
→ Output (dist/ folder)
npm — Package Management
npm init -y
npm install lodash
npm install -D vite typescript eslint
# Run scripts from package.json
npm run dev
npm run build
package.json Scripts
{
"name": "my-app",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src/",
"test": "vitest"
},
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"vite": "^5.0.0",
"typescript": "^5.3.0"
}
}
- dependencies — required at runtime
- devDependencies — build/test tools only
- Lock file (
package-lock.json) pins exact versions
Vite (Recommended for New Projects)
Fast dev server with native ESM; production build via Rollup:
npm create vite@latest my-app -- --template vanilla
cd my-app
npm install
npm run dev
vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
root: '.',
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['lodash']
}
}
}
},
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:8080'
}
}
});
Vite features: instant HMR, CSS modules, PostCSS, TypeScript, environment variables.
Environment Variables
# .env
VITE_API_URL=https://api.example.com
const apiUrl = import.meta.env.VITE_API_URL;
Prefix with VITE_ to expose to client code. Never put secrets in frontend env vars.
Webpack (Legacy/Enterprise)
Still widely used in existing codebases:
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
},
plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
optimization: {
splitChunks: { chunks: 'all' }
}
};
Webpack is powerful but complex — prefer Vite for greenfield projects.
Transpilation
Convert modern syntax for older browsers:
| Tool | Purpose |
|---|---|
| esbuild | Ultra-fast JS/TS transpile (used by Vite) |
| Babel | Flexible plugin-based transpilation |
| TypeScript (tsc) | Type checking + emit JS |
Babel Example
{
"presets": [
["@babel/preset-env", { "targets": "> 0.5%, not dead" }]
]
}
Use browserslist in package.json to define target browsers.
Bundling Concepts
Tree Shaking
Remove unused exports from bundles:
// Only debounce included if that's all you import
import { debounce } from 'lodash-es';
Use ES modules (import/export) — CommonJS (require) limits tree shaking.
Code Splitting
Load code on demand:
const module = await import('./heavy-chart.js');
module.renderChart(data);
Vite/Webpack automatically split dynamic imports into separate chunks.
Content Hashing
bundle.a1b2c3.js — cache busting when content changes.
CSS in Build Pipeline
// Vite supports CSS modules out of the box
import styles from './Button.module.css';
// PostCSS (autoprefixer, tailwind) via postcss.config.js
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
Linting and Formatting
npm install -D eslint prettier eslint-config-prettier
// .eslintrc.json
{
"env": { "browser": true, "es2022": true },
"extends": ["eslint:recommended", "prettier"],
"parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }
}
Run in CI: npm run lint && npm run build && npm test.
Production Build Checklist
-
npm run buildsucceeds without warnings - Source maps generated (upload to error tracker, not public)
- Environment variables set for production
- Assets compressed (gzip/Brotli on server)
- Bundle analyzed — no accidental full-library imports
- Lighthouse performance audit passes
Analyze Bundle Size
npm install -D rollup-plugin-visualizer
# or
npx vite-bundle-visualizer
Deployment
npm run build
# dist/ folder contains static assets
# Deploy to: Netlify, Vercel, S3+CloudFront, Nginx, GitHub Pages
# Nginx SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
Monorepos (Overview)
Tools: npm workspaces, pnpm, Turborepo, Nx
{
"workspaces": ["packages/*", "apps/*"]
}
Share code between apps while independent versioning.
Best Practices
- Pin dependencies — commit lock file
- Separate dev/prod configs — no dev tools in production bundle
- Analyze bundle regularly — catch size regressions
- Use CI/CD — automated build, test, deploy
- Keep tooling updated — security patches in dependencies
Troubleshooting
Module not found
- Check import path and file extension; verify package installed
HMR not working
- Circular dependencies; restart dev server
Build works locally, fails in CI
- Node version mismatch — specify in
.nvmrcorengines
Huge bundle size
- Check for importing entire libraries; enable tree shaking; code split
Build tools are the foundation of professional frontend delivery — master npm and Vite (or your team’s stack) for efficient development and optimized production output.