ITSkillsCenter
Blog

Vérifier votre environnement

8 min de lecture

Ce que vous saurez faire à la fin

  1. Maîtriser les nouvelles syntaxes ES2024 (Object.groupBy, Promise.withResolvers, Array.fromAsync) et savoir quand les utiliser.
  2. Refactorer des dizaines de boucles legacy en code moderne lisible, divisant la dette technique de votre projet par deux.
  3. Écrire des async pipelines robustes avec les nouvelles API de Promesses, sans dépendances externes.
  4. Configurer Node.js 22 LTS et un bundler moderne pour exploiter les nouvelles features sur les navigateurs ciblés en Afrique.
  5. Détecter automatiquement les anciennes pratiques avec ESLint et migrer un projet PME progressivement sans casser la production.

Durée : 3h. Pré-requis : Node.js 22 ou plus, npm/pnpm, VS Code, connaissance de base de JavaScript ES6 (let, const, arrow functions, async/await), un projet existant pour pratiquer. Coût : 0 FCFA.

Étape 1 — Vérifier votre environnement

ES2024 est le nom de la 15ème édition du standard ECMAScript, publiée officiellement en juin 2024. Avant de coder, assurez-vous que votre runtime supporte ces nouveautés. Node.js 22 LTS, sorti en octobre 2024, supporte 100% d’ES2024. Pour les navigateurs, Chrome 117+, Firefox 119+ et Safari 17+ couvrent l’essentiel.

node --version
# v22.11.0 ou plus récent

npm --version
# 10.9.0 ou plus

# Créer un projet de test
mkdir es2024-tuto && cd es2024-tuto
npm init -y
npm pkg set type=module
echo "console.log('Node', process.version)" > index.js
node index.js

Si vous bloquez sur Node.js ancien (10, 12, 14), installez nvm pour gérer plusieurs versions : votre projet legacy continue de tourner pendant que vous testez ES2024 dans un nouveau dossier.

Étape 2 — Object.groupBy : grouper sans Lodash

Avant ES2024, grouper un tableau d’objets par champ exigeait soit Lodash (importer 70 Ko juste pour groupBy), soit un reduce verbeux. Object.groupBy règle ce besoin en natif.

const ventes = [
  { mois: 'Janvier', produit: 'Riz', montant: 450000 },
  { mois: 'Janvier', produit: 'Huile', montant: 120000 },
  { mois: 'Février', produit: 'Riz', montant: 520000 },
  { mois: 'Février', produit: 'Huile', montant: 95000 }
];

// Avant : reduce verbeux
const parMoisLegacy = ventes.reduce((acc, v) => {
  acc[v.mois] = acc[v.mois] || [];
  acc[v.mois].push(v);
  return acc;
}, {});

// ES2024 : une ligne
const parMois = Object.groupBy(ventes, v => v.mois);
console.log(parMois);
// { Janvier: [...], Février: [...] }

Pour grouper avec une clé non-string (Date, objet), utilisez Map.groupBy qui retourne une Map au lieu d’un objet plain. Cas typique : grouper des transactions par client identifié par un objet User.

Étape 3 — Promise.withResolvers : promesses externalisées

Vous connaissez le pattern « deferred » : créer une Promise dont vous gardez les fonctions resolve et reject pour les appeler plus tard depuis ailleurs. ES2024 le standardise.

// Avant : capturer resolve manuellement
function attendreClick() {
  let resolve, reject;
  const promesse = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  document.querySelector('#valider')
    .addEventListener('click', () => resolve(), { once: true });
  return promesse;
}

// ES2024 : syntaxe officielle
function attendreClickV2() {
  const { promise, resolve, reject } = Promise.withResolvers();
  document.querySelector('#valider')
    .addEventListener('click', () => resolve(), { once: true });
  return promise;
}

Cas d’usage typique pour une PME e-commerce : attendre la confirmation d’un paiement Wave ou Orange Money via webhook, sans bloquer le thread principal de la commande.

Étape 4 — Array.fromAsync pour itérables asynchrones

// Lire toutes les lignes d'un fichier de commandes en streaming
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

async function* lignesCSV(path) {
  const rl = createInterface({ input: createReadStream(path) });
  for await (const ligne of rl) {
    yield ligne.split(',');
  }
}

// ES2024 : un seul appel
const lignes = await Array.fromAsync(lignesCSV('commandes.csv'));
console.log(`${lignes.length} commandes chargées`);

// Equivalent legacy laborieux
const lignesLegacy = [];
for await (const l of lignesCSV('commandes.csv')) {
  lignesLegacy.push(l);
}

Pour un import de stock de 50 000 lignes depuis un fournisseur chinois ou un export comptable Sage, Array.fromAsync rend le code 3 fois plus court et plus lisible.

Étape 5 — RegExp v flag (Unicode Sets)

// Détecter les emojis dans les commentaires clients (Wolof + Français)
const texte = "Merci beaucoup ! Service au top, livraison rapide. Mashallah";

// ES2024 : flag v + propriétés Unicode
const regexEmoji = /\p{RGI_Emoji}/v;
console.log(regexEmoji.test(texte));

// Combinaisons et différences ensemblistes
const lettresAccentuees = /[\p{Letter}--\p{ASCII}]/v;
const motSenegalais = "Téranga";
console.log(motSenegalais.match(lettresAccentuees));
// ['é']

// Détecter caractères Arabe pour clientèle bilingue
const arabe = /\p{Script=Arabic}+/v;
console.log("Bonjour مرحبا".match(arabe));
// ['مرحبا']

Le flag v améliore aussi la robustesse Unicode : un emoji composé (drapeau, famille) est traité comme une unité, plus comme deux caractères orphelins. Important pour valider les pseudos d’utilisateurs sur une plateforme qui mélange Wolof, Français et Arabe.

Étape 6 — Symbols comme clés WeakMap

// Avant : seuls les objets pouvaient être clés WeakMap
const cache = new WeakMap();
const utilisateur = { id: 42 };
cache.set(utilisateur, { lastSeen: Date.now() });

// ES2024 : les Symbols uniques aussi
const tokenSession = Symbol('session-utilisateur-42');
cache.set(tokenSession, { ip: '41.82.x.x', expires: Date.now() + 3600000 });

// Pratique : associer des métadonnées privées sans pollution
const metaPrive = Symbol();
class Commande {
  constructor(id) {
    this.id = id;
    this[metaPrive] = { auditTrail: [] };
  }
}

Cette feature débloque des patterns avancés de programmation par capacités, où chaque Symbol représente une autorisation unique impossible à forger.

Étape 7 — String.isWellFormed et toWellFormed

// Avant : encoder une URL avec surrogate orphelin plante l'API
const cassé = "Hello \uD800 World"; // surrogate seul

try {
  encodeURI(cassé);
} catch (e) {
  console.error('URIError:', e.message);
}

// ES2024 : vérifier puis nettoyer
if (!cassé.isWellFormed()) {
  const propre = cassé.toWellFormed();
  // Remplace les surrogates orphelins par U+FFFD
  encodeURI(propre); // OK
}

// Cas réel : sanitiser des données importées d'un CSV mal encodé
function nettoyerNom(nom) {
  return nom.isWellFormed() ? nom : nom.toWellFormed();
}

Étape 8 — ArrayBuffer.prototype.transfer

// Transférer la propriété d'un buffer (zéro copie)
const buf1 = new ArrayBuffer(1024 * 1024); // 1 Mo
const view = new Uint8Array(buf1);
view.fill(42);

// ES2024 : transférer sans copier
const buf2 = buf1.transfer(2 * 1024 * 1024); // resize à 2 Mo
console.log(buf1.detached); // true
console.log(buf2.byteLength); // 2 097 152

// Cas usage : passer une image traitée à un Worker sans la dupliquer
const worker = new Worker('./traitement.js');
worker.postMessage({ image: buf2 }, [buf2]);

Pour une PME qui traite des images produits côté client (compression, redimensionnement avant upload), cette API divise la consommation mémoire par 2 sur les vieux Android encore très répandus dans la région.

Étape 9 — Atomics.waitAsync pour SharedArrayBuffer

// Synchronisation entre threads sans bloquer l'event loop
const sab = new SharedArrayBuffer(4);
const i32 = new Int32Array(sab);

// Ancien : Atomics.wait bloque le thread (interdit sur le main thread)
// ES2024 : version asynchrone
const { async, value } = Atomics.waitAsync(i32, 0, 0);

if (async) {
  value.then(result => console.log('Notifié :', result));
} else {
  console.log('Pas d\'attente :', value);
}

// Depuis un Worker, réveiller le main thread :
// Atomics.notify(i32, 0, 1);

Étape 10 — Configurer ESLint pour ES2024

npm install -D eslint @eslint/js
# eslint.config.js
cat > eslint.config.js << 'EOF'
import js from '@eslint/js';
export default [
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: 'module',
      globals: { console: 'readonly', process: 'readonly' }
    },
    rules: {
      'prefer-const': 'error',
      'no-var': 'error',
      'no-unused-vars': 'warn'
    }
  }
];
EOF
npx eslint .

Étape 11 — Bundler avec Vite ciblant ES2024

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

export default defineConfig({
  build: {
    target: ['chrome117', 'firefox119', 'safari17'],
    minify: 'esbuild'
  },
  esbuild: {
    target: 'es2024'
  }
});

Pour un site PME ciblant majoritairement des smartphones Android récents, viser ES2024 supprime des polyfills inutiles et réduit le bundle de 30 à 50 Ko, ce qui compte sur une connexion 3G à Touba ou Saint-Louis.

Étape 12 — Migration progressive sans casser la prod

// utils/groupBy.js : wrapper sécurisé
export const groupBy = (arr, keyFn) => {
  if (typeof Object.groupBy === 'function') {
    return Object.groupBy(arr, keyFn);
  }
  // Fallback pour anciens navigateurs
  return arr.reduce((acc, item) => {
    const k = keyFn(item);
    (acc[k] ||= []).push(item);
    return acc;
  }, {});
};

// Dans le code applicatif
import { groupBy } from './utils/groupBy.js';
const facturesParClient = groupBy(factures, f => f.clientId);

Cette stratégie d’adoption progressive vous permet d’utiliser ES2024 dès aujourd’hui même si une partie de vos clients reste sur Safari 15 ou un Android avec Chrome 100. Quand les stats Analytics montrent moins de 1% d’anciens navigateurs, supprimez les fallbacks.

Étape 13 — Tests automatisés avec Vitest

npm install -D vitest
# tests/groupBy.test.js
cat > tests/groupBy.test.js << 'EOF'
import { test, expect } from 'vitest';
import { groupBy } from '../utils/groupBy.js';

test('groupe par champ', () => {
  const data = [{ v: 'a', x: 1 }, { v: 'b', x: 2 }, { v: 'a', x: 3 }];
  const res = groupBy(data, d => d.v);
  expect(res.a).toHaveLength(2);
  expect(res.b).toHaveLength(1);
});
EOF
npx vitest run

Erreurs fréquentes

  • Object.groupBy is not a function : votre Node.js est trop ancien (moins de 21). Mettez à jour ou utilisez le fallback de l’étape 12.
  • SyntaxError: Invalid regular expression flag : le flag v n’est pas reconnu par votre moteur. Ciblez Chrome 112+, Firefox 116+, Safari 17+.
  • SharedArrayBuffer is not defined : requiert les en-têtes COOP et COEP côté serveur. Sur Vercel ou Netlify, ajoutez Cross-Origin-Opener-Policy: same-origin et Cross-Origin-Embedder-Policy: require-corp.
  • Tree-shaking inefficace : certains polyfills core-js gonflent le bundle. Configurez browserslist en production-only pour cibler des navigateurs récents et Vite supprimera 70% des polyfills.
  • Promise.withResolvers retourne undefined : support manquant sur Safari 17.0 (mais OK depuis 17.4). Polyfill : npm install promise-with-resolvers-polyfill.

Checklist de validation

  • Node.js 22 LTS installé et version vérifiée avec node –version
  • package.json contient « type »: « module » pour utiliser import/export
  • ESLint configuré avec ecmaVersion 2024 et exécuté sans erreur sur le projet
  • Au moins 3 reduce ou boucles for legacy remplacés par Object.groupBy
  • Une Promise externe refactorée avec Promise.withResolvers
  • Browserslist ou target Vite ajusté pour profiter du tree-shaking ES2024
  • Tests Vitest passent au vert sur les utilitaires migrés
  • Stats Analytics consultées pour estimer le pourcentage de navigateurs incompatibles
  • Fallback prévu pour les 1 à 5% d’utilisateurs sur ancien Android ou iOS
  • Documentation interne mise à jour avec les conventions ES2024 adoptées
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité