Construction et packaging¶
La chaîne de build JavaScript et TypeScript couvre la gestion des dépendances, la compilation, le bundling, la publication et le déploiement. Ce chapitre compare les outils modernes et fournit des configurations pret-a-l'emploi.
Gestionnaires de paquets¶
| Outil | Vitesse install | Disk usage | Workspace | Lockfile | Points forts |
|---|---|---|---|---|---|
| npm | Lente | Élevée | Oui | package-lock.json | Standard, ubiquiste, inclus avec Node.js |
| pnpm | Très rapide | Faible | Excellent | pnpm-lock.yaml | Hard links, strict isolation, monorepos |
| yarn | Rapide | Moyenne | Bon | yarn.lock | PnP mode, workspaces, corepack |
| bun | Ultra rapide | Faible | Oui | bun.lockb | Compatible npm, tout-en-un |
Commandes équivalentes¶
# Installation des dependances
npm install # npm
pnpm install # pnpm
yarn # yarn
bun install # bun
# Ajout d'une dependance
npm install zod # npm
pnpm add zod # pnpm
yarn add zod # yarn
bun add zod # bun
# Ajout d'une dependance de dev
npm install -D vitest # npm
pnpm add -D vitest # pnpm
yarn add -D vitest # yarn
bun add -d vitest # bun
# Execution d'un script
npm run dev # npm
pnpm dev # pnpm (sans 'run' pour les scripts)
yarn dev # yarn
bun dev # bun
Recommandation
pnpm est recommande pour la plupart des projets en 2024 : économie d'espace disque via hard links, isolation stricte des dépendances qui evite les bugs de hoisting, excellent support des monorepos. bun pour les environnements de dev ou la vitesse est prioritaire.
package.json et structure de projet¶
// package.json — Configuration complete pour un projet TypeScript
{
"name": "@monorg/mon-paquet",
"version": "1.0.0",
"description": "Description du projet",
"type": "module",
"license": "MIT",
// Points d'entree selon le contexte
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
},
// Fichiers inclus dans le paquet npm
"files": ["dist", "README.md"],
"engines": { "node": ">=22.0.0" },
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsup src/index.ts --format esm,cjs --dts",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build && npm test"
},
"dependencies": {
"zod": "^3.23.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"tsup": "^8.0.0",
"typescript": "^5.6.0",
"vitest": "^2.0.0"
}
}
TypeScript : tsconfig.json¶
// tsconfig.json — Configuration TypeScript stricte pour Node.js 22+
{
"compilerOptions": {
// Target : ES2022 est bien supporte par Node.js 22
"target": "ES2022",
"module": "Node16", // Gestion des imports ESM et CJS
"moduleResolution": "Node16",
// Strictness — activer tout en mode strict
"strict": true, // active tous les flags strict
"noUncheckedIndexedAccess": true, // index peut etre undefined
"exactOptionalPropertyTypes": true, // distingue undefined et absent
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
// Chemins
"rootDir": "./src",
"outDir": "./dist",
// Declarations pour les bibliotheques
"declaration": true,
"declarationMap": true,
"sourceMap": true,
// Interoperabilite
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
// Chemins alias (optionnel)
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
Bundlers¶
| Outil | Langage | DX dev | Perf build | Tree-shaking | Points forts |
|---|---|---|---|---|---|
| Vite | Rollup | Native HMR | Rapide | Excellent | Dev ultra-rapide, plugins, frameworks |
| esbuild | Go | Basique | Ultra-rapide | Bon | Plus rapide bundler, API simple |
| tsup | esbuild | Bon | Ultra-rapide | Bon | Zero config pour bibliotheques TypeScript |
| Rollup | JS | Moyen | Moyen | Excellent | Optimal pour bibliotheques, output propre |
| Webpack 5 | JS | Plugin | Lent | Bon | Maturité, écosystème, cas complexes |
Vite — application frontend¶
// vite.config.ts — Vite 6 pour une application React
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'node:path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
target: 'es2022',
outDir: 'dist',
rollupOptions: {
output: {
// Chunking manuel pour optimiser le cache navigateur
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
server: {
port: 5173,
proxy: {
// Proxy vers l'API en dev pour eviter les CORS
'/api': 'http://localhost:3000',
},
},
});
tsup — bibliotheque TypeScript¶
// tsup.config.ts — tsup pour publier une bibliotheque
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'], // Point d'entree
format: ['esm', 'cjs'], // Dual format
dts: true, // Genere les .d.ts
splitting: false, // Pas de code splitting pour libs
sourcemap: true,
clean: true, // Nettoie dist/ avant build
minify: false, // Pas de minification pour libs (lisibilite)
target: 'node22',
external: ['prisma', '@prisma/client'], // Dependances externes non bundlees
});
Publication npm¶
# 1. Creer un compte npmjs.com et se connecter
npm login
# 2. Verifier ce qui sera publie
npm pack --dry-run
# 3. Publier
npm publish --access public # paquets sous @scope
npm publish # paquets publics non-scopes
# 4. Versionner avec npm version
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.0 -> 1.1.0
npm version major # 1.0.0 -> 2.0.0
# .npmignore — Fichiers exclus du paquet npm
src/
tests/
*.test.ts
tsconfig.json
tsup.config.ts
.env*
Dockerfile multi-stage¶
# Dockerfile — Build multi-stage pour une API Node.js 22
FROM node:22-alpine AS base
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
# Stage : installation des dependances
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Stage : build TypeScript
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build
RUN pnpm prune --prod
# Stage : image de production (minimale)
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Utilisateur non-root pour la securite
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Copier uniquement le necessaire
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
# docker-compose.yml — Dev avec hot-reload
services:
api:
build:
context: .
target: deps # Stage de dev avec toutes les deps
command: pnpm dev
ports:
- "3000:3000"
volumes:
- ./src:/app/src # Hot reload
environment:
- NODE_ENV=development
- DATABASE_URL=file:./dev.db
Pipeline CI/CD¶
# .github/workflows/ci.yml — GitHub Actions
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type checking
run: pnpm typecheck
- name: Lint
run: pnpm lint
- name: Tests avec coverage
run: pnpm test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Build
run: pnpm build
docker:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build et push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: monorg/mon-api:latest
Lockfiles en CI
Toujours utiliser --frozen-lockfile (pnpm) ou npm ci (npm) en CI pour garantir des builds reproductibles. npm install peut mettre à jour les versions de paquets et introduire des régressions silencieuses.