nutriscore-2025
Version:
Calculateur TypeScript pour le Nutri-Score 2025 avec support des catégories spéciales (viandes rouges, boissons, matières grasses, fromages)
291 lines (290 loc) • 10.9 kB
JavaScript
"use strict";
// Nutri-Score 2025 – TypeScript complet avec améliorations
// Basé sur les règles officielles du Nutri-Score 2025
// Sources : Santé publique France, Commission européenne
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductCategory = void 0;
var ProductCategory;
(function (ProductCategory) {
ProductCategory["GENERAL"] = "general";
ProductCategory["FATS"] = "fats";
ProductCategory["CHEESE"] = "cheese";
ProductCategory["BEVERAGES"] = "beverages";
})(ProductCategory = exports.ProductCategory || (exports.ProductCategory = {}));
function calculeNutriscore(nutriments) {
// Validation des valeurs d'entrée
if (nutriments.energy < 0 ||
nutriments.fibers < 0 ||
nutriments.proteins < 0 ||
nutriments.saturatedFats < 0 ||
nutriments.sodium < 0 ||
nutriments.sugar < 0) {
throw new Error('Les valeurs nutritionnelles ne peuvent pas être négatives');
}
var category = nutriments.category || ProductCategory.GENERAL;
var isRedMeat = nutriments.isRedMeat || false;
var containsEdulcorants = nutriments.containsEdulcorants || false;
var isWater = nutriments.isWater || false;
var ferHeminique = nutriments.ferHeminique || 0;
var energy = nutriments.energy * 4.184;
var fibers = Math.max(0, nutriments.fibers);
var proteins = Math.max(0, nutriments.proteins);
var saturatedFats = Math.max(0, nutriments.saturatedFats);
var sodium = Math.max(0, nutriments.sodium);
var sugar = Math.max(0, nutriments.sugar);
var fruitsPercentage = Math.min(Math.max(nutriments.fruitsPercentage, 0), 100);
var totalFats = Math.max(0, nutriments.totalFats || 0);
switch (category) {
case ProductCategory.GENERAL:
return calculateGeneralNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, isRedMeat, ferHeminique);
case ProductCategory.FATS:
return calculateFatsNutriscore(fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, totalFats);
case ProductCategory.CHEESE:
return calculateCheeseNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage);
case ProductCategory.BEVERAGES:
return calculateBeveragesNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, containsEdulcorants, isWater);
default:
return calculateGeneralNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, isRedMeat, ferHeminique);
}
}
exports.default = calculeNutriscore;
function calculateGeneralNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, isRedMeat, ferHeminique) {
var energyScore = calculateEnergyScore(energy);
var sugarScore = calculateGeneralSugarScore(sugar);
var saturatedFatsScore = calculateSaturatedFatsScore(saturatedFats);
var sodiumScore = calculateSodiumScore(sodium);
var fibersScore = calculateFibersScore(fibers);
var proteinsScore = calculateProteinsScore(proteins);
var fruitsScore = calculateFruitsScore(fruitsPercentage);
if (isRedMeat && ferHeminique < 1 && proteinsScore > 2) {
proteinsScore = 2;
}
var pScore = fibersScore + proteinsScore + fruitsScore;
var nScore = energyScore + sugarScore + saturatedFatsScore + sodiumScore;
if (nScore >= 11)
pScore = fibersScore + fruitsScore;
var finalScore = nScore - pScore;
return getNutriscoreGradeGeneric(finalScore, [0, 2, 10, 18]);
}
function calculateFatsNutriscore(fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, totalFats) {
var energyFromSaturatedFats = saturatedFats * 37;
var energyScore = calculateEnergyScoreForFats(energyFromSaturatedFats);
var satFatRatio = totalFats > 0 ? (saturatedFats / totalFats) * 100 : 0;
var satFatRatioScore = calculateSatFatRatioScore(satFatRatio);
var sugarScore = calculateGeneralSugarScore(sugar);
var sodiumScore = calculateSodiumScore(sodium);
var fibersScore = calculateFibersScore(fibers);
var proteinsScore = calculateProteinsScore(proteins);
var fruitsScore = calculateFruitsScore(fruitsPercentage);
var nScore = energyScore + sugarScore + satFatRatioScore + sodiumScore;
var pScore = fibersScore + proteinsScore + fruitsScore;
if (nScore >= 7)
pScore = fibersScore + fruitsScore;
var finalScore = nScore - pScore;
return getNutriscoreGradeGeneric(finalScore, [-6, 2, 10, 18]);
}
function calculateCheeseNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage) {
var energyScore = calculateEnergyScore(energy);
var sugarScore = calculateGeneralSugarScore(sugar);
var saturatedFatsScore = calculateSaturatedFatsScore(saturatedFats);
var sodiumScore = calculateSodiumScore(sodium);
var fibersScore = calculateFibersScore(fibers);
var proteinsScore = calculateProteinsScore(proteins);
var fruitsScore = calculateFruitsScore(fruitsPercentage);
var nScore = energyScore + sugarScore + saturatedFatsScore + sodiumScore;
var pScore = fibersScore + proteinsScore + fruitsScore;
var finalScore = nScore - pScore;
return getNutriscoreGradeGeneric(finalScore, [0, 2, 10, 18]);
}
function calculateBeveragesNutriscore(energy, fibers, proteins, saturatedFats, sodium, sugar, fruitsPercentage, containsEdulcorants, isWater) {
if (isWater) {
return {
nutriscore: 'A',
logoNutriscore: 'https://static.openfoodfacts.org/images/attributes/dist/nutriscore-a-new-en.svg',
};
}
var energyScore = calculateEnergyScoreForBeverages(energy);
var sugarScore = calculateSugarScoreForBeverages(sugar);
var saturatedFatsScore = calculateSaturatedFatsScore(saturatedFats);
var sodiumScore = calculateSodiumScore(sodium);
var fibersScore = calculateFibersScore(fibers);
var proteinsScore = calculateProteinsScoreForBeverages(proteins);
var fruitsScore = calculateFruitsScoreForBeverages(fruitsPercentage);
var nScore = energyScore + sugarScore + saturatedFatsScore + sodiumScore;
if (containsEdulcorants)
nScore += 4;
var pScore = fibersScore + proteinsScore + fruitsScore;
var finalScore = nScore - pScore;
return getNutriscoreGradeGeneric(finalScore, [2, 6, 9], ['B', 'C', 'D', 'E']);
}
// Barèmes Nutri-Score 2025
// Énergie (kJ/100g) - Conversion kcal → kJ (× 4.184)
function calculateEnergyScore(energy) {
return energy <= 335 ? 0 : Math.min(10, Math.ceil((energy - 335) / 335));
}
// Énergie pour matières grasses (kJ/100g)
function calculateEnergyScoreForFats(energy) {
return energy <= 120 ? 0 : Math.min(10, Math.ceil((energy - 120) / 120));
}
// Énergie pour boissons (kJ/100g)
function calculateEnergyScoreForBeverages(energy) {
return energy <= 30 ? 0 : Math.min(10, Math.ceil((energy - 30) / 60));
}
// Sucre (g/100g) - Produits généraux
function calculateGeneralSugarScore(sugar) {
if (sugar <= 3.4)
return 0;
if (sugar <= 6.8)
return 1;
if (sugar <= 10)
return 2;
if (sugar <= 14)
return 3;
if (sugar <= 17)
return 4;
if (sugar <= 20)
return 5;
if (sugar <= 24)
return 6;
if (sugar <= 27)
return 7;
if (sugar <= 31)
return 8;
if (sugar <= 34)
return 9;
if (sugar <= 37)
return 10;
if (sugar <= 41)
return 11;
if (sugar <= 44)
return 12;
if (sugar <= 48)
return 13;
if (sugar <= 51)
return 14;
return 15;
}
// Sucre (g/100g) - Boissons
function calculateSugarScoreForBeverages(sugar) {
if (sugar <= 0.5)
return 0;
if (sugar <= 2)
return 1;
if (sugar <= 3.5)
return 2;
if (sugar <= 5)
return 3;
if (sugar <= 6)
return 4;
if (sugar <= 7)
return 5;
if (sugar <= 8)
return 6;
if (sugar <= 9)
return 7;
if (sugar <= 10)
return 8;
if (sugar <= 11)
return 9;
return 10;
}
// Acides gras saturés (g/100g)
function calculateSaturatedFatsScore(val) {
return Math.min(10, Math.floor(val));
}
// Ratio acides gras saturés/total (%) - Matières grasses
function calculateSatFatRatioScore(ratio) {
return Math.min(10, Math.floor((ratio - 10) / 6));
}
// Sodium (mg/100g) - Limité à 20 points max
function calculateSodiumScore(val) {
return Math.min(20, Math.floor(val / 90));
}
// Fibres (g/100g)
function calculateFibersScore(val) {
if (val <= 3)
return 0;
if (val <= 4.1)
return 1;
if (val <= 5.2)
return 2;
if (val <= 6.3)
return 3;
if (val <= 7.4)
return 4;
return 5;
}
// Protéines (g/100g) - Produits généraux
function calculateProteinsScore(val) {
if (val <= 2.4)
return 0;
if (val <= 4.8)
return 1;
if (val <= 7.2)
return 2;
if (val <= 9.6)
return 3;
if (val <= 12)
return 4;
if (val <= 14)
return 5;
if (val <= 17)
return 6;
return 7;
}
// Protéines (g/100g) - Boissons
function calculateProteinsScoreForBeverages(val) {
if (val <= 1.2)
return 0;
if (val <= 1.5)
return 1;
if (val <= 1.8)
return 2;
if (val <= 2.1)
return 3;
if (val <= 2.4)
return 4;
if (val <= 2.7)
return 5;
if (val <= 3)
return 6;
return 7;
}
// Fruits, légumes, légumineuses et fruits à coque (%)
function calculateFruitsScore(val) {
if (val <= 40)
return 0;
if (val <= 60)
return 1;
if (val <= 80)
return 2;
return 5;
}
// Fruits, légumes, légumineuses et fruits à coque (%) - Boissons
function calculateFruitsScoreForBeverages(val) {
if (val <= 40)
return 0;
if (val <= 60)
return 2;
if (val <= 80)
return 4;
return 6;
}
// Conversion du score final en lettre Nutri-Score
// thresholds: seuils de classification (score ≤ seuil)
// letters: lettres correspondantes (A, B, C, D, E)
function getNutriscoreGradeGeneric(score, thresholds, letters) {
if (letters === void 0) { letters = ['A', 'B', 'C', 'D', 'E']; }
for (var i = 0; i < thresholds.length; i++) {
if (score <= thresholds[i]) {
return {
nutriscore: letters[i],
logoNutriscore: "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-".concat(letters[i].toLowerCase(), "-new-en.svg"),
};
}
}
return {
nutriscore: letters[letters.length - 1],
logoNutriscore: "https://static.openfoodfacts.org/images/attributes/dist/nutriscore-".concat(letters[letters.length - 1].toLowerCase(), "-new-en.svg"),
};
}