UNPKG

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
"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"), }; }