Aller au contenu

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.