13. Build Tools & Development Environment
13. Build Tools & Development Environment¶
Learning Objectives¶
- Understand and utilize package managers (npm, yarn, pnpm)
- Configure modern build tools (Vite, webpack)
- Set up and optimize development environment
- Manage environment variables
- Optimize production builds
Table of Contents¶
1. Package Managers¶
1.1 npm (Node Package Manager)¶
# Initialize project
npm init
npm init -y # Initialize with defaults
# Install packages
npm install lodash # Add to dependencies
npm install -D typescript # Add to devDependencies
npm install -g create-vite # Install globally
# Shorthand commands
npm i lodash
npm i -D typescript
# Remove packages
npm uninstall lodash
npm rm lodash
# Update packages
npm update # All packages
npm update lodash # Specific package
npm outdated # Check available updates
# Run scripts
npm run dev
npm run build
npm test # Shorthand for npm run test
# Package info
npm info lodash
npm list # Installed package tree
npm list --depth=0 # Top-level only
1.2 package.json¶
{
"name": "my-project",
"version": "1.0.0",
"description": "Project description",
"main": "dist/index.js",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src/**/*.{js,ts}",
"format": "prettier --write src/**/*.{js,ts}",
"test": "vitest",
"prepare": "husky install"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"typescript": "^5.0.0",
"vite": "^5.0.0"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/user/repo.git"
},
"keywords": ["react", "vite", "typescript"],
"author": "Your Name <email@example.com>",
"license": "MIT"
}
1.3 Version Management¶
Version format: MAJOR.MINOR.PATCH (1.2.3)
package.json version ranges:
^1.2.3 → 1.x.x (Allow MINOR and PATCH updates)
~1.2.3 → 1.2.x (Allow PATCH updates only)
1.2.3 → Exactly 1.2.3
>=1.2.3 → 1.2.3 or higher
1.2.x → 1.2.0 ~ 1.2.999
* → Any version
Best practices:
- Production: Commit package-lock.json
- Libraries: Use range specifiers (^)
1.4 yarn¶
# Install Yarn
npm install -g yarn
# Basic commands
yarn init
yarn add lodash
yarn add -D typescript
yarn remove lodash
yarn upgrade
yarn # = yarn install
# Yarn Workspaces (Monorepo)
# package.json
{
"workspaces": [
"packages/*"
]
}
# Run workspace packages
yarn workspace @myorg/web add react
yarn workspaces foreach run build
1.5 pnpm¶
# Install pnpm
npm install -g pnpm
# Basic commands
pnpm init
pnpm add lodash
pnpm add -D typescript
pnpm remove lodash
pnpm update
pnpm install
# pnpm advantages
# - Disk space savings (hard links)
# - Fast installation speed
# - Strict dependency management
# pnpm Workspaces
# pnpm-workspace.yaml
packages:
- 'packages/*'
2. Vite¶
2.1 Introduction to Vite¶
┌─────────────────────────────────────────────────────────────────┐
│ Vite Features │
│ │
│ Development Server: │
│ - Uses Native ES Modules (no bundling) │
│ - Instant HMR (Hot Module Replacement) │
│ - Fast cold starts │
│ │
│ Production Build: │
│ - Rollup-based optimization │
│ - Code splitting │
│ - Tree shaking │
│ │
│ Support: │
│ - TypeScript, JSX, CSS Modules │
│ - React, Vue, Svelte, etc. │
│ - Plugin system │
└─────────────────────────────────────────────────────────────────┘
2.2 Creating a Project¶
# Create Vite project
npm create vite@latest my-app
# Specify template directly
npm create vite@latest my-app -- --template react-ts
npm create vite@latest my-app -- --template vue-ts
npm create vite@latest my-app -- --template svelte-ts
# Project structure
my-app/
├── node_modules/
├── public/
│ └── vite.svg
├── src/
│ ├── App.tsx
│ ├── main.tsx
│ └── vite-env.d.ts
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts
2.3 vite.config.ts¶
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
// Development server settings
server: {
port: 3000,
open: true,
cors: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// Build settings
build: {
outDir: 'dist',
sourcemap: true,
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'dayjs'],
},
},
},
},
// Path aliases
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils'),
},
},
// CSS settings
css: {
modules: {
localsConvention: 'camelCase',
},
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
// Optimization settings
optimizeDeps: {
include: ['lodash', 'axios'],
exclude: ['@vite/client'],
},
});
2.4 TypeScript Configuration¶
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* Path aliases */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
2.5 Static Asset Handling¶
// Importing images
import logo from './assets/logo.png'; // Returns URL
import icon from './assets/icon.svg?raw'; // SVG string
// public folder (copied without processing)
// public/favicon.ico → /favicon.ico
// Referencing assets in CSS
.bg {
background-image: url('@/assets/bg.png');
}
// Dynamic URLs
const imgUrl = new URL('./img.png', import.meta.url).href;
3. webpack Basics¶
3.1 Introduction to webpack¶
┌─────────────────────────────────────────────────────────────────┐
│ webpack Concepts │
│ │
│ Entry: Entry point (starting file) │
│ Output: Location of bundled result │
│ Loaders: Transform non-JS files (CSS, images, etc.) │
│ Plugins: Bundle optimization, environment variable injection │
│ Mode: development / production │
│ │
│ How it works: │
│ Entry → Analyze dependency graph → Apply Loaders → │
│ Execute Plugins → Generate Output │
└─────────────────────────────────────────────────────────────────┘
3.2 Basic Configuration¶
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// Mode
mode: 'development', // or 'production'
// Entry point
entry: './src/index.js',
// Output
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true, // Clean previous build files
},
// Loaders
module: {
rules: [
// JavaScript/TypeScript
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: 'babel-loader',
},
// CSS
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
// SCSS
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
// Images
{
test: /\.(png|jpg|gif|svg)$/,
type: 'asset/resource',
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
},
],
},
// Plugins
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
// Dev server
devServer: {
static: './dist',
port: 3000,
hot: true,
open: true,
},
// Module resolution
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
// Source maps
devtool: 'source-map',
};
3.3 Production Optimization¶
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = merge(common, {
mode: 'production',
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
},
},
}),
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
}),
// Bundle analysis (when needed)
// new BundleAnalyzerPlugin(),
],
});
4. Environment Variables¶
4.1 Vite Environment Variables¶
# .env (all environments)
VITE_APP_NAME=My App
# .env.development (development)
VITE_API_URL=http://localhost:8080
# .env.production (production)
VITE_API_URL=https://api.example.com
# .env.local (local, gitignore)
VITE_SECRET_KEY=my-secret
// Using environment variables
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE; // 'development' | 'production'
const isDev = import.meta.env.DEV; // boolean
const isProd = import.meta.env.PROD; // boolean
// Type definitions
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
4.2 webpack Environment Variables¶
// webpack.config.js
const webpack = require('webpack');
const dotenv = require('dotenv');
// Load .env file
const env = dotenv.config().parsed;
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify(env),
}),
],
};
// Or individual variables
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.API_URL': JSON.stringify(process.env.API_URL),
});
4.3 Environment-specific Configuration¶
// config/index.ts
interface Config {
apiUrl: string;
debug: boolean;
features: {
newDashboard: boolean;
};
}
const configs: Record<string, Config> = {
development: {
apiUrl: 'http://localhost:8080',
debug: true,
features: {
newDashboard: true,
},
},
production: {
apiUrl: 'https://api.example.com',
debug: false,
features: {
newDashboard: false,
},
},
};
export const config = configs[import.meta.env.MODE] || configs.development;
5. Code Quality Tools¶
5.1 ESLint¶
# Install
npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
# Initialize
npx eslint --init
// eslint.config.js (Flat Config - ESLint 9+)
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import react from 'eslint-plugin-react';
export default [
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
plugins: {
react,
},
rules: {
'no-unused-vars': 'warn',
'no-console': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'react/prop-types': 'off',
},
},
{
ignores: ['dist/**', 'node_modules/**'],
},
];
5.2 Prettier¶
# Install
npm install -D prettier eslint-config-prettier
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf"
}
// .prettierignore
node_modules
dist
build
coverage
*.min.js
5.3 Husky + lint-staged¶
# Install
npm install -D husky lint-staged
# Initialize Husky
npx husky install
# Add pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
// package.json
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,md,json}": [
"prettier --write"
]
}
}
5.4 EditorConfig¶
# .editorconfig
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
6. Practice Problems¶
Exercise 1: Vite Project Setup¶
Set up a React + TypeScript project with Vite.
Answer
# Example solution
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
# Additional packages
npm install -D @types/node
npm install axios react-router-dom
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
},
});
Exercise 2: Environment Variable Setup¶
Configure development/production API URLs.
Answer
# .env.development
VITE_API_URL=http://localhost:8080/api
# .env.production
VITE_API_URL=https://api.myapp.com/api
// src/config.ts
export const config = {
apiUrl: import.meta.env.VITE_API_URL,
isDev: import.meta.env.DEV,
};
// src/api/client.ts
import axios from 'axios';
import { config } from '../config';
export const apiClient = axios.create({
baseURL: config.apiUrl,
});
Exercise 3: Code Quality Tools Setup¶
Set up ESLint + Prettier + Husky.
Answer
# Install
npm install -D eslint prettier eslint-config-prettier
npm install -D husky lint-staged
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
# Husky setup
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
// package.json
{
"scripts": {
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix",
"format": "prettier --write src/**/*.{ts,tsx,css}",
"prepare": "husky install"
},
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,json,md}": ["prettier --write"]
}
}