Node.js 20, devenu LTS en octobre 2023, apporte deux features importantes : un modèle de permissions (sandboxing) pour restreindre ce qu'un programme Node peut faire, et la stabilisation du Test Runner intégré. L'écosystème ESM continue de mûrir.
1. Permission Model — Sandboxing de Node.js (JEP expérimental)
Inspiré de Deno, Node 20 introduit un système de permissions optionnel. En le activant, vous devez autoriser explicitement chaque accès : filesystem, réseau, processus enfants…
# Sans permission : toutes les opérations bloquées
node --experimental-permission app.js
# Autoriser la lecture d'un dossier spécifique
node --experimental-permission \
--allow-fs-read=/data \
app.js
# Autoriser lecture ET écriture
node --experimental-permission \
--allow-fs-read=/tmp \
--allow-fs-write=/tmp \
app.js
# Autoriser les processus enfants
node --experimental-permission \
--allow-child-process \
app.js
# Autoriser l'accès réseau sortant
node --experimental-permission \
--allow-net \
app.js
# Vérifier une permission en cours d'exécution
node --experimental-permission \
--allow-fs-read=/data app.js
import { permission } from 'node:process';
// Vérifier dynamiquement avant d'accéder
if (permission.has('fs.read', '/etc/passwd')) {
// on peut lire ce fichier
} else {
console.warn('Accès refusé au fichier');
}
// Types de permissions disponibles
permission.has('fs.read'); // accès lecture système de fichiers
permission.has('fs.write'); // accès écriture
permission.has('net'); // accès réseau
permission.has('child_process'); // spawn/exec
permission.has('worker'); // Worker threads
Le Permission Model est idéal pour les architectures de plugins ou pour exécuter du code tiers de manière sécurisée. Il complète (mais ne remplace pas) des solutions comme Docker pour l'isolation.
2. Test Runner — Stable et enrichi
Le test runner passe de expérimental à stable en Node 20, avec de nouvelles fonctionnalités : reporters, snapshot testing, coverage natif.
import { describe, it, before, after, mock } from 'node:test';
import assert from 'node:assert/strict';
describe('UserService', () => {
let service;
before(async () => {
service = new UserService();
await service.init();
});
after(async () => {
await service.cleanup();
});
it('crée un utilisateur', async () => {
const user = await service.create({ name: 'Kriss' });
assert.ok(user.id);
assert.equal(user.name, 'Kriss');
});
it('mock un appel externe', async t => {
// Mock natif — plus besoin de jest.mock() ou sinon
t.mock.method(service, 'fetchExternalApi', async () => ({
data: 'mocked'
}));
const result = await service.fetchExternalApi();
assert.equal(result.data, 'mocked');
assert.equal(service.fetchExternalApi.mock.callCount(), 1);
});
});
# Lancer avec reporters
# node --test --test-reporter=tap
# node --test --test-reporter=junit --test-reporter-destination=report.xml
# node --test --experimental-test-coverage
3. import.meta.resolve() stable
import.meta.resolve() résout un chemin de module relatif au fichier courant, de manière synchrone.
// Résoudre le chemin d'un fichier de données relatif au module
const dataPath = import.meta.resolve('./data/config.json');
// → file:///home/user/projet/src/data/config.json
// Résoudre un package npm
const lodashPath = import.meta.resolve('lodash');
// Utilisation avec fs
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
const config = JSON.parse(
readFileSync(
fileURLToPath(import.meta.resolve('./config.json')),
'utf-8'
)
);
// Plus simple qu'avant !
// import { createRequire } from 'node:module';
// const require = createRequire(import.meta.url);
4. V8 11.3 — Nouvelles méthodes Array
// Array.fromAsync — créer un tableau depuis un itérable async
const arr = await Array.fromAsync(readLines(file));
// groupBy (via Object.groupBy / Map.groupBy)
const personnes = [
{ name: 'Alice', dept: 'dev' },
{ name: 'Bob', dept: 'ops' },
{ name: 'Charlie', dept: 'dev' }
];
const parDept = Object.groupBy(personnes, p => p.dept);
// { dev: [{name:'Alice',...}, {name:'Charlie',...}], ops: [...] }
// Méthodes "to" — versions immuables
const arr2 = [3, 1, 4, 1, 5];
arr2.toSorted(); // [1, 1, 3, 4, 5] — arr2 inchangé !
arr2.toReversed(); // [5, 1, 4, 1, 3] — arr2 inchangé
arr2.toSpliced(1, 1, 99); // [3, 99, 4, 1, 5] — arr2 inchangé
arr2.with(2, 99); // [3, 1, 99, 1, 5] — arr2 inchangé
5. Récapitulatif Node.js 20
Permission Model
Sandboxing: contrôle granulaire des accès fs, réseau, processus.
Test Runner stable
describe/it/mock natifs, reporters, coverage — zero dépendance.
import.meta.resolve()
Résolution synchrone de chemins de modules en ESM.
V8 11.3
Array.fromAsync, Object.groupBy, toSorted, toReversed — API immuables.