diff --git a/package.json b/package.json index 75f5da648..c013cc1f0 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "react-redux": "^7.0.3", "react-router": "^5.1.1", "react-router-dom": "^5.1.1", + "react-router-hash-link": "^1.2.2", "react-spring": "=8.0.27", "react-syntax-highlighter": "^10.1.1", "react-to-print": "^2.5.1", diff --git a/publicode/mecanism.md b/publicode/mecanism.md index 4fd40f923..085f5521a 100644 --- a/publicode/mecanism.md +++ b/publicode/mecanism.md @@ -159,6 +159,8 @@ brut: somme: - 2000 €/mois | du 01/01/2020 | au 31/05/2020 - 4000 €/mois | du 01/06/2020 | au 31/12/2020 +plafond: + formule: 3000 €/mois cotisation: formule: @@ -166,13 +168,17 @@ cotisation: règle: produit: assiette: brut - plafond: 3000€/mois + plafond: plafond taux: 10% valeurs cumulées: - brut + - plafond -cotisation en 2020: - formule: cotisation | du 01/01/2020 | au 31/12/2020 +cotisation en mai: + formule: cotisation | du 01/05/2020 | au 30/05/2020 + +cotisation en juin: + formule: cotisation | du 01/05/2020 | au 30/05/2020 ``` [Lancer le calcul](https://publi.codes/studio?code=brut%3A%0A%20%20formule%3A%0A%20%20%20%20somme%3A%0A%20%20%20%20%20%20-%202000%20%E2%82%AC%2Fmois%20%7C%20du%2001%2F01%2F2020%20%7C%20au%2031%2F05%2F2020%0A%20%20%20%20%20%20-%204000%20%E2%82%AC%2Fmois%20%7C%20du%2001%2F06%2F2020%20%7C%20au%2031%2F12%2F2020%0A%0Acotisation%3A%0A%20%20formule%3A%20%0A%20%20%20%20r%C3%A9gularisation%3A%0A%20%20%20%20%20%20r%C3%A8gle%3A%0A%20%20%20%20%20%20%20%20produit%3A%0A%20%20%20%20%20%20%20%20%20%20assiette%3A%20brut%0A%20%20%20%20%20%20%20%20%20%20plafond%3A%203000%E2%82%AC%2Fmois%0A%20%20%20%20%20%20%20%20%20%20taux%3A%2010%25%0A%20%20%20%20%20%20valeurs%20cumul%C3%A9es%3A%0A%20%20%20%20%20%20%20%20-%20brut%0A%0Acotisation%20en%202020%3A%0A%20%20formule%3A%0A%20%20%20%20cotisation%20%7C%20du%2001%2F01%2F2020%20%7C%20au%2031%2F12%2F2020%0A) diff --git a/source/components/Mecanisms.tsx b/source/components/Mecanisms.tsx deleted file mode 100644 index 62ad97a6f..000000000 --- a/source/components/Mecanisms.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Page listing the engine's currently implemented mecanisms and their tests -import knownMecanims from 'Engine/known-mecanisms.yaml' -import { fromPairs, has, toPairs } from 'ramda' -import React from 'react' -import './Mecanisms.css' - -let directoryLoader = require.context('../../test/mécanismes/', true, /.yaml$/), - suites = fromPairs( - directoryLoader - .keys() - .map(key => [ - key.replace(/\/|\.|(yaml)/g, '').replace(/-/g, ' '), - directoryLoader(key) - ]) - ) - -export default function Mecanisms() { - return ( -
-

- Cette page liste les mécanismes et indique en rouge ceux qui n'ont pas - de tests. La commande "yarn test" permet de voir ceux qui passent. Ce - serait bien de pouvoir les faire tourner dans le navigateur en - partageant le code de mecanisms.test.js -

- -
- ) -} - -function Tests({ suites, name }) { - let suite = suites[name], - tests = suite.filter(has('test')) - - return ( -

- {tests.length} {tests.length == 1 ? 'test' : 'tests'} -

- ) -} diff --git a/source/components/rule/ColoredYaml.tsx b/source/components/rule/ColoredYaml.tsx deleted file mode 100644 index f3575f509..000000000 --- a/source/components/rule/ColoredYaml.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { Light as SyntaxHighlighter } from 'react-syntax-highlighter' -import yaml from 'react-syntax-highlighter/dist/cjs/languages/hljs/yaml' -import style from 'react-syntax-highlighter/dist/cjs/styles/hljs/tomorrow' -SyntaxHighlighter.registerLanguage('yaml', yaml) - -export default ({ source }) => ( - - {source} - -) diff --git a/source/components/rule/Rule.js b/source/components/rule/Rule.js index 6d29ee017..57e36d469 100644 --- a/source/components/rule/Rule.js +++ b/source/components/rule/Rule.js @@ -1,7 +1,7 @@ import { ThemeColorsContext } from 'Components/utils/colors' import { SitePathsContext } from 'Components/utils/withSitePaths' import Value from 'Components/Value' -import knownMecanisms from 'Engine/known-mecanisms.yaml' +import mecanisms from 'Engine/mecanisms.yaml' import { findRuleByDottedName, findRuleByNamespace } from 'Engine/rules' import { isEmpty } from 'ramda' import React, { Suspense, useContext, useState } from 'react' @@ -28,7 +28,7 @@ import './Rule.css' let LazySource = React.lazy(() => import('./RuleSource')) -export default AttachDictionary(knownMecanisms)(function Rule({ dottedName }) { +export default AttachDictionary(mecanisms)(function Rule({ dottedName }) { const currentExample = useSelector(state => state.currentExample) const flatRules = useSelector(flatRulesSelector) const valuesToShow = !useSelector(noUserInputSelector) diff --git a/source/components/rule/RuleSource.tsx b/source/components/rule/RuleSource.tsx index 846665af9..b94754824 100644 --- a/source/components/rule/RuleSource.tsx +++ b/source/components/rule/RuleSource.tsx @@ -3,7 +3,7 @@ import rules from 'Publicode/rules' import React from 'react' import emoji from 'react-easy-emoji' import { Rule } from 'Types/rule' -import ColoredYaml from './ColoredYaml' +import PublicodeHighlighter from '../ui/PublicodeHighlighter' type RuleSourceProps = Pick @@ -17,7 +17,7 @@ export default function RuleSource({ dottedName }: RuleSourceProps) { Code source
{dottedName} - + ) } diff --git a/source/components/ui/PublicodeHighlighter.tsx b/source/components/ui/PublicodeHighlighter.tsx new file mode 100644 index 000000000..6ba266e62 --- /dev/null +++ b/source/components/ui/PublicodeHighlighter.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import emoji from 'react-easy-emoji' +import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter' +import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml' +import style from 'react-syntax-highlighter/dist/esm/styles/prism/atom-dark' + +SyntaxHighlighter.registerLanguage('yaml', yaml) + +export default ({ source }) => ( +
+ + {source} + + + {emoji('⚡')} Lancer le calcul + +
+) diff --git a/source/components/ui/index.css b/source/components/ui/index.css index c525eea08..54810e000 100644 --- a/source/components/ui/index.css +++ b/source/components/ui/index.css @@ -31,10 +31,11 @@ button { blockquote { background: var(--lighterColor); - border-radius: 0.6em; - padding: 1.2em 1em 0.4em; - margin: 0.6em; - color: #333; + border-radius: 0.3rem; + padding: 1rem; + padding-bottom: 0.6rem; + margin: 1rem 0; + color: var(--darkerColor); } .ui__.answer-group { diff --git a/source/components/utils/markdown.tsx b/source/components/utils/markdown.tsx index 6b77b8c2d..3e4347cb6 100644 --- a/source/components/utils/markdown.tsx +++ b/source/components/utils/markdown.tsx @@ -1,3 +1,4 @@ +import PublicodeHighlighter from 'Components/ui/PublicodeHighlighter' import React from 'react' import emoji from 'react-easy-emoji' import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown' @@ -31,6 +32,15 @@ type MarkdownProps = ReactMarkdownProps & { className?: string } +const CodeBlock = ({ value, language }) => + language === 'yaml' ? ( + + ) : ( +
+			{value}
+		
+ ) + export const Markdown = ({ source, className = '', @@ -40,7 +50,12 @@ export const Markdown = ({ ) diff --git a/publicode/README.md b/source/engine/README.md similarity index 72% rename from publicode/README.md rename to source/engine/README.md index 1f7ed2f03..ea6895eb1 100644 --- a/publicode/README.md +++ b/source/engine/README.md @@ -1,16 +1,29 @@ -# Publicode - Publicode est un langage déclaratif pour encoder les algorithmes d'intérêt public. Il permet de réaliser des calculs généraux tout en fournissant une explication permettant de comprendre et de documenter ces calculs. Publicode est adapté pour modéliser des domaines métiers complexes pouvant être -décomposés en règles élémentaires simples (comme la législation socio-fiscale, -un bilan carbone, un estimateur de rendement locatif, etc.). Il permet de +décomposés en règles élémentaires simples (comme la [législation socio-fiscale](https://github.com/betagouv/mon-entreprise/tree/master/publicode), +[un bilan carbone](https://github.com/laem/futureco-data/blob/master/co2.yaml), +un estimateur de rendement locatif, etc.). + +Il permet de générer facilement des simulateurs web interactifs où l'on peut affiner progressivement le résultat affiché, et explorer une documentation du calcul. -## Formule basiques +## Projets phares + +- [mon-entreprise.fr](https://mon-entreprise.fr/simulateurs) : utilise publicode + pour spécifier l'ensemble des calculs relatifs à la législation socio-fiscale + en France. Le site permet entre autre de simuler une fiche de paie complète, + de calculer les cotisations sociales pour un indépendant ou encore connaître + le montant du chômage partiel. Les règles sont publiées sous la forme d'une + [bibliothèque de calcul autonome](https://mon-entreprise.fr/intégration/bibliothèque-de-calcul), libre de droit. + +- [futur.eco](https://futur.eco/) utilise publicode pour calculer les bilans + carbone d'un grand nombre d'activités, plats, transports ou biens. + +## Principe de base La syntaxe de Publicode est basée sur le langage [Yaml](https://en.wikipedia.org/wiki/YAML). Un fichier Publicode contient une @@ -18,7 +31,7 @@ liste de règles identifiées par leur nom et possédant une formule de calcul : ```yaml prix d'un repas: - formule: 10 + formule: 10 € ``` Une formule de calcul peut référencer d'autres variables. Dans l'exemple suivant @@ -26,14 +39,12 @@ la règle `prix total` aura pour valeur 50 (= 5 \* 10) ```yaml prix d'un repas: - formule: 10 + formule: 10 € prix total: formule: 5 * prix d'un repas ``` -> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%0A%0Aprix%20total%3A%0A%20%20formule%3A%205%20*%20prix%20d'un%20repas) - Il s'agit d'un langage déclaratif : comme dans une formule d'un tableur le `prix total` sera recalculé automatiquement si le prix d'un repas change. L'ordre de définition des règles n'a pas d'importance. @@ -53,12 +64,12 @@ prix total: formule: nombre de repas * prix d'un repas ``` -> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%20%E2%82%AC%2Frepas%0A%0Anombre%20de%20repas%3A%0A%20%20formule%3A%205%20repas%0A%0Aprix%20total%3A%0A%20%20formule%3A%20nombre%20de%20repas%20*%20prix%20d'un%20repas) - Le calcul est inchangé mais on a indiqué que le "prix d'un repas" s'exprime en `€/repas` et que le "nombre de repas" est un nombre de `repas`. L'unité du prix total est inférée automatiquement comme étant en `€`. (`€/repas` \* `repas` = -`€`) Ce système d'unité permet de typer les formules de calcul et de rejeter +`€`) + +Ce système d'unité permet de typer les formules de calcul et de rejeter automatiquement des formules incohérentes : ```yaml @@ -77,8 +88,6 @@ prix total: # La formule de "prix total" est invalide. ``` -> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%20%E2%82%AC%2Frepas%0A%0Anombre%20de%20repas%3A%0A%20%20formule%3A%205%20repas%0A%0Afrais%20de%20r%C3%A9servation%3A%0A%20%20formule%3A%201%20%E2%82%AC%2Frepas%0A%0Aprix%20total%3A%0A%20%20formule%3A%20nombre%20de%20repas%20*%20prix%20d'un%20repas%20%2B%20frais%20de%20r%C3%A9servation) - Dans l'exemple ci-dessus Publicode détecte une erreur car les termes de l'addition ont des unités incompatibles : d'un côté on a des `€` et de l'autre des `€/repas`. Comme dans les formules de Physique, cette incohérence d'unité @@ -90,10 +99,37 @@ prix total: formule: nombre de repas * (prix d'un repas + frais de réservation) ``` -> [Lancer le calcul]() +> **Attention :** Il ne faut pas insérer d'espace autour de la barre oblique dans +> les unités, l'unité `€ / mois` doit être notée `€/mois` -> **Attention:** Il ne faut pas insérer d'espace autour de la barre oblique dans -> les unités, l'unité ~`€ / mois`~ doit être notée `€/mois` +### Conversion + +Publicode convertit automatiquement les unités si besoin. + +```yaml +salaire: + formule: 1500 €/mois + +prime faible salaire: + applicable si: salaire < 20 k€/an + formule: 300€ +``` + +On peut forcer la conversion des unités via la propriété `unité`, ou la notation suffixé `[...]` + +```yaml +salaire: + unité: €/mois + formule: 3200 + +salaire annuel: + formule: salaire [k€/an] +``` + +**Conversions disponibles :** + +- `mois` / `année` / `jour` +- `€` / `k€` ## Titre, description et références @@ -149,25 +185,34 @@ spécifique par mécanisme. Par exemple on a un mécanisme `barème`: ```yaml +revenu imposable: + formule: 54126 € + impôt sur le revenu: formule: barème: assiette: revenu imposable tranches: - taux: 0% - plafond: 9807 + plafond: 9807 € - taux: 14% - plafond: 27086 + plafond: 27086 € - taux: 30% - plafond: 72617 + plafond: 72617 € - taux: 41% - plafond: 153783 + plafond: 153783 € - taux: 45% ``` La syntaxe hiérarchique de Yaml permet d'imbriquer les mécanismes : ```yaml +prime . fixe: + formule: 1000€ + +prime . taux du bonus: + formule: 20% + prime: formule: somme: @@ -175,15 +220,9 @@ prime: - produit: assiette: fixe taux: taux du bonus - -prime . fixe: - formule: 1000€ - -prime . taux du bonus: - formule: 20% ``` -**[Voir la liste des mécanismes](https://github.com/betagouv/mon-entreprise/blob/master/publicode/mecanism.md)** +> **[Aller à la liste des mécanismes existants](./mécanismes)** ## Applicabilité @@ -204,8 +243,6 @@ prime de vacances: formule: 200€ ``` -> [Lancer le calcul](https://publi.codes/studio?code=date%20de%20d%C3%A9but%3A%20%0A%20%20formule%3A%2012%2F02%2F2020%0A%20%20%0Aanciennet%C3%A9%20en%20fin%20d'ann%C3%A9e%3A%0A%20%20formule%3A%20%0A%20%20%20%20dur%C3%A9e%3A%0A%20%20%20%20%20%20%20depuis%3A%20date%20de%20d%C3%A9but%0A%20%20%20%20%20%20%20jusqu'%C3%A0%3A%2031%2F12%2F2020%0A%0Aprime%20de%20vacances%3A%0A%20%20applicable%20si%3A%20anciennet%C3%A9%20en%20fin%20d'ann%C3%A9e%20%3E%201%20an%0A%20%20formule%3A%20200%E2%82%AC) - Ici si l'ancienneté est inférieure à un an la prime de vacances ne sera pas applicable. Les variables non applicables sont ignorées au niveau des mécanismes (par exemple le mécanisme `somme` comptera une prime non applicable comme valant diff --git a/source/engine/known-mecanisms.yaml b/source/engine/known-mecanisms.yaml deleted file mode 100644 index e493831a0..000000000 --- a/source/engine/known-mecanisms.yaml +++ /dev/null @@ -1,219 +0,0 @@ -# Liste et description des différents mécanismes compris par le moteur. -# La description peut être rédigée en markdown :-) - -une possibilité: - type: enum - -inversion numérique: - type: numeric - description: | - La formule de calcul de cette variable n'est pas connue, souvent elle n'a même pas de sens. Mais le mécanisme `inversion` indique qu'elle peut être _estimée_ à partir de l'un des _objectifs_ listés sous l'attribut `avec`. Il faut alors renseigner une valeur cible pour cet objectif. - - Voilà comment ça marche : on va donner à la variable une valeur au hasard, calculer _l'objectif_, puis grâce à des calculs savants améliorer notre choix jusqu'à ce que l'écart entre le calcul et la valeur cible devienne satisfaisant. - - Concrètement, si l'on demande au moteur (même indirectement) la valeur d'une variable qui a pour formule une inversion, il va vérifier qu'une des possibilités d'inversion a bien une valeur calculée ou saisie, et procéder à l'inversion décrite plus haut à partir de celle-ci. Sinon, ces possibilités d'inversions seront listées comme manquantes. - -une de ces conditions: - type: boolean - description: | - C'est un `ou` logique. - - Contient une liste de conditions. - - Renvoie vrai si l'une des conditions est vraie. -toutes ces conditions: - type: boolean - description: | - C'est un `et` logique. - - Contient une liste de conditions. - - Renvoie vrai si toutes les conditions vraies. - -variations: - type: numeric - description: | - Contient une liste de conditions (`si`) et leurs conséquences associées (`alors`). - - Pour la première condition vraie dans la liste, on retient la valeur qui lui est associée. - - Si aucune condition n'est vraie, alors ce mécanisme renvoie implicitement `non applicable` - - - Exemple: - ``` - - si: age < 18 ans - alors: 'mineur' - - sinon: 'majeur' - ``` - - Ce mécanisme peut aussi être utilisé au sein d'un mécanisme compatible, tel que la produit ou le barème. - - Exemple (TVA): - ``` - produit: - assiette: total hors taxe - variations: - - si: restauration - alors: - taux: 10% - - sinon: - taux: 20% - ``` - -produit: - type: numeric - description: | - C'est une multiplication un peu améliorée, très utile pour exprimer les cotisations. - - Sa propriété `assiette` est multipliée par un pourcentage, `taux`, ou par un `facteur` quand ce nom est plus approprié. - - La multiplication peut être plafonnée : ce plafond sépare l'assiette en deux, et la partie au-dessus du plafond est tout simplement ignorée. Dans ce cas, elle se comporte comme une barème en taux marginaux à deux tranches, la deuxième au taux nul et allant de `plafond` à l'infini. - -le maximum de: - type: numeric - description: | - Renvoie la valeur numérique de la liste de propositions fournie qui est la plus grande. - - Il est conseillé de renseigner une description de chaque proposition par exemple quand elles représentent des méthodes de calcul alternatives. - -le minimum de: - type: numeric - description: | - Renvoie l'élément de la liste de propositions fournie qui a la plus petite valeur. - - Ces propositions doivent avoir un mécanisme de calcul ou être une valeur numérique. - - Il est conseillé de renseigner une description de chaque proposition par exemple quand elles représentent des méthodes de calcul alternatives parmi lesquelles il faut en choisir une. - -somme: - type: numeric - description: | - C'est tout simplement la somme de chaque terme de la liste. - -arrondi: - type: numeric - description: | - On arrondi à l'euro le plus proche - -recalcul: - type: numeric - description: >- - Relance le calcul d'une règle dans une situation différente de la situation - courante. Permet par exemple de calculer le montant des cotisations au - niveau du SMIC, même si le salaire est plus élevé dans la situation - actuelle. - -########################################## -# Ce qu'on appelle aujourd'hui des RuleProp -# Et qui deviendront des mécanismes classiques normalement par la suite #TODO - -formule: - description: | - C'est la formule de calcul d'une variable. Elle renvoie une valeur numérique ou un 'non', exprimant le fait que la variable n'est pas applicable, ce qui vaut implicitement 0. - - Cette doit faire appel à fera appel à des mécanismes de calcul : par exemple `produit`, le plus commun pour les variables de type `Cotisation`. - -applicable si: - description: | - Décide si la règle est applicable pour la situation saisie. - - > Une cotisation sociale peut ne concerner que les salariés au statut cadre. Elle ne sera pas à verser pour un non cadre - - La formule de calcul peut donc être ignorée, quel que soit son montant. - - Peut être accompagnée du mécanisme 'non applicable si'. - -non applicable si: - description: | - Décide si la règle n'est applicable pour la situation saisie. - - > Un contrat CDD peut entraîner une majoration spécifique. Cette majoration est 'non applicable si' le contrat est un CDI. - - La formule de calcul peut donc être ignorée, quel que soit son montant. - - Peut être accompagnée du mécanisme 'applicable si'. - -rend non applicable: - description: | - Permet de désactiver l'application de certaines règles pour la situation saisie. - - > Ce mécanisme est utile pour encoder les régimes d'exceptions (par exemple le [régime des impatriés](/documentation/contrat-salarié/régime-des-impatriés)) sans avoir à modifier la définition des règles de base. - -barème: - type: numeric - description: >- - C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation - dans le calcul de l'impôt sur le revenu. - - L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un - taux spécifique. - - Les tranches sont souvent exprimées sous forme de facteurs d'une variable - que l'on appelle `multiplicateur`, par exemple `1 x le plafond de la - sécurité sociale`. - -grille: - type: numeric - description: >- - C'est un barème sous la forme d'une grille de correspondance. C'est le - mécanisme de calcul de l'impôt neutre, aussi appelé impôt non personnalisé. - - Il est composé de tranches qui se suivent. Il suffit de trouver l'assiette - qui correspond à la tranche, et de selectionner le montant associé à - l'assiette. - -taux progressif: - type: numeric - description: >- - Ce mécanisme permet de calculer un taux progressif. On spécifie pour chaque - tranche le plafond et le taux associé. Le taux effectif renvoyé est calculé - en lissant la différence de taux entre la borne inférieure et supérieure de - l'assiette - - > Par exemple, si nous nous avons les tranches suivantes : - - taux: 50% / plafond: 0 - - taux: 100% / plafond: 1000 - - > Pour une assiette de 500, le taux retourné sera 75%, car il correspond au - taux situé à la moitié de la tranche correspondante. - -composantes: - type: numeric - description: | - Beaucoup de cotisations sont composées de deux parties qui partagent la méthode de calcul mais diffèrent par des paramètres différents. - - Pour ne pas définir deux variables presque redondantes, on utilise le mécanisme de composante. Il se comportera comme une somme dans les calculs, mais son affichage sur les pages /règle sera adapté. - - Il est même possible, pour les mécanismes `barème` et `produit` de garder en commun un paramètre comme l'assiette, puis de déclarer des composantes pour le taux. - - > L'exemple le plus courant de composantes, c'est la distinction part employeur, part salarié (ex. retraite AGIRC). - -allègement: - type: numeric - description: | - Permet de réduire le montant d'une variable. - Très utilisé dans le contexte des impôts. - -encadrement: - type: numeric - description: | - Permet d'ajouter un plafond et/ou un plancher à une valeur. - -durée: - type: numeric - description: | - Permet d'obtenir le nombre de jours entre deux dates - -synchronisation: - type: object - description: | - Pour éviter trop de saisies à l'utilisateur, certaines informations sont - récupérées à partir de ce que l'on appelle des API. Ce sont des services - auxquels ont fait appel pour obtenir des informations sur un sujet précis. - Par exemple, l'État français fournit gratuitement l'API géo, qui permet à - partir du nom d'une ville, d'obtenir son code postal, son département, la - population etc. - - Ce mécanismes `synchronisation` permet de faire le lien entre les règles de - notre système et les réponses de ces API. diff --git a/source/engine/mecanisms.js b/source/engine/mecanisms.js index f2f93505f..3e6f54f9e 100644 --- a/source/engine/mecanisms.js +++ b/source/engine/mecanisms.js @@ -2,33 +2,11 @@ import { decompose } from 'Engine/mecanisms/utils' import variations from 'Engine/mecanisms/variations' import { convertNodeToUnit } from 'Engine/nodeUnits' import { inferUnit, isPercentUnit } from 'Engine/units' -import { - any, - equals, - evolve, - is, - map, - max, - mergeWith, - min, - path, - pluck, - reduce, - toPairs -} from 'ramda' +import { any, equals, evolve, is, map, max, mergeWith, min, path, pluck, reduce, toPairs } from 'ramda' import React from 'react' import 'react-virtualized/styles.css' import { typeWarning } from './error' -import { - collectNodeMissing, - defaultNode, - evaluateArray, - evaluateNode, - evaluateObject, - makeJsx, - mergeAllMissing, - parseObject -} from './evaluation' +import { collectNodeMissing, defaultNode, evaluateArray, evaluateNode, evaluateObject, makeJsx, mergeAllMissing, parseObject } from './evaluation' import Allègement from './mecanismViews/Allègement' import { Node, SimpleRuleLink } from './mecanismViews/common' import InversionNumérique from './mecanismViews/InversionNumérique' @@ -463,7 +441,7 @@ export let mecanismReduction = (recurse, k, v) => { export let mecanismProduct = (recurse, k, v) => { if (v.composantes) { - //mécanisme de composantes. Voir known-mecanisms.md/composantes + //mécanisme de composantes. Voir mécanismes.md/composantes return decompose(recurse, k, v) } if (v.variations) { diff --git a/source/engine/mecanisms.yaml b/source/engine/mecanisms.yaml new file mode 100644 index 000000000..46725ce62 --- /dev/null +++ b/source/engine/mecanisms.yaml @@ -0,0 +1,437 @@ +une de ces conditions: + description: >- + C'est un `ou` logique. + Contient une liste de conditions. + Renvoie `oui` si l'une des conditions est applicable. + retourne: Booléen + exemples: + base: >- + âge: + formule: 17 ans + + mineur émancipé: + formule: oui + + peut voter: + formule: + une de ces conditions: + - âge > 18 ans + - mineur émancipé + +toutes ces conditions: + description: >- + C'est un `et` logique. + Contient une liste de conditions. + Renvoie `oui` si toutes les conditions sont applicables. + argument: + - '*' + - ... + exemples: + base: >- + âge: + formule: 17 ans + + citoyenneté française: + formule: oui + + peut voter: + formule: + toutes ces conditions: + - citoyenneté française + - âge > 18 ans + +produit: + description: >- + C'est une multiplication un peu améliorée, très utile pour exprimer les + cotisations. + + Sa propriété `assiette` est multipliée par un pourcentage, `taux`, ou par un + `facteur` quand ce nom est plus approprié. + + La multiplication peut être plafonnée : ce plafond sépare l'assiette en + deux, et la partie au-dessus du plafond est tout simplement ignorée. Dans ce + cas, elle se comporte comme une barème en taux marginaux à deux tranches, la + deuxième au taux nul et allant de `plafond` à l'infini. + argument: + assiette: Valeur à multiplier + taux: Taux à appliquer + facteur: Facteur multiplicatif + plafond: Plafond au dessus duquel le taux appliqué est nul + + exemples: + base: >- + cotisation: + formule: + produit: + assiette: 2000 €/mois + taux: 5% + + assiette plafonnée: >- + plafond sécurité sociale: + formule: 3000 €/mois + + assiette cotisation: + formule: 15000 €/mois + + chômage: + formule: + produit: + assiette: assiette cotisation + plafond: 400% * plafond sécurité sociale + taux: 4% + +variations: + description: >- + Contient une liste de conditions (`si`) et leurs conséquences associées + (`alors`). + + Pour la première condition vraie dans la liste, on retient la valeur qui lui + est associée. + + Si aucune condition n'est vraie, alors ce mécanisme renvoie implicitement + `non applicable` + + Ce mécanisme peut aussi être utilisé au sein d'un mécanisme compatible, tel qu'un produit ou un barème. + arguments: + - si: condition à vérifier + alors: consequence évaluée si la condition est vrai + - ... + - sinon: consequence évaluée si aucune des conditions précédente n'était applicable + exemples: + base: >- + taux réduit: + formule: oui + + taux allocation familiales: + formule: + variations: + - si: taux réduit + alors: 3.45% + - sinon: 5.25% + + dans un produit: >- + assiette cotisation: + formule: 2300 €/mois + + taux réduit: + formule: oui + + allocation familiales: + formule: + produit: + assiette: assiette cotisation + variations: + - si: taux réduit + alors: + taux: 3.45% + - sinon: + taux: 5.25% + +somme: + description: >- + C'est tout simplement la somme de chaque terme de la liste. Si un des terme + n'est pas applicable, il vaut zéro. + + On peut aussi retrancher des valeurs avec l'opérateur unaire `-` + arguments: + - '*' + - ... + + exemples: + base: >- + somme: + - 15.89 € + - 12% * 14 € + - (-30 €) + terme non applicable: >- + a: + formule: 50 € + + b: + applicable si: non + formule: 20 € + + somme: + formule: + somme: + - a + - b + - 40 € + +le maximum de: + description: >- + Renvoie la valeur numérique de la liste de propositions fournie qui est la + plus grande. + + Pour ajouter un plancher à une valeur, préférer l'utilisation du + mécanisme `encadrement`. + exemples: + base: >- + max: + formule: + le maximum de: + - 50 + - 100 + +le minimum de: + description: >- + Renvoie la valeur numérique de la liste de propositions fournie qui est la + plus petite. + + Pour plafonner une valeur, préférer l'utilisation du mécanisme `encadrement`. + exemples: + base: >- + min: + formule: + le minimum de: + - 50 + - 100 + +arrondi: + description: >- + Arrondi à l'entier le plus proche, ou à une précision donnée. + exemples: + base: >- + arrondi: + formule: + arrondi: + valeur: 12.45 + décimales: 1 + +régularisation: + description: >- + Permet de régulariser progressivement un calcul de cotisation en fonction de + variables numérique mensuelle cumulée. + + Ce mécanisme spécifique est utilisé pour le calcul des cotisations + mensuelles. + + arguments: + règle: règle à régulariser + valeurs cumulées: + - liste de variables cumulée mensuellement pour calculer la régularisation. Doit être + numérique, et avoir une unité `/mois` + + exemples: + base: >- + brut: + formule: + somme: + - 2000 €/mois | du 01/01/2020 | au 31/05/2020 + - 4000 €/mois | du 01/06/2020 | au 31/12/2020 + plafond: + formule: 3000 €/mois + + cotisation: + formule: + régularisation: + règle: + produit: + assiette: brut + plafond: plafond + taux: 10% + valeurs cumulées: + - brut + - plafond + + cotisation en mai: + formule: cotisation | du 01/05/2020 | au 31/05/2020 + + cotisation en juin: + formule: cotisation | du 01/06/2020 | au 30/06/2020 + + cotisation en novembre: + formule: cotisation | du 01/11/2020 | au 30/11/2020 + +recalcul: + description: >- + Relance le calcul d'une règle dans une situation différente de la situation + courante. Permet par exemple de calculer le montant des cotisations au niveau du + SMIC, même si le salaire est plus élevé dans la situation actuelle. + + exemples: + base: >- + brut: + formule: 2000€ + + cotisations: + formule: + produit: + assiette: brut + taux: 20% + + cotisations pour un SMIC: + recalcul: + règle: cotisations + avec: + - brut: 1500 € + +barème: + description: C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation + dans le calcul de l'impôt sur le revenu. + + L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un + taux spécifique. + + Les tranches sont souvent exprimées sous forme de facteurs d'une variable + que l'on appelle `multiplicateur`, par exemple `1 x le plafond de la + sécurité sociale`. + exemples: + base: >- + revenu imposable: + formule: 54126 € + + impôt sur le revenu: + formule: + barème: + assiette: revenu imposable + tranches: + - taux: 0% + plafond: 9807 € + - taux: 14% + plafond: 27086 € + - taux: 30% + plafond: 72617 € + - taux: 41% + plafond: 153783 € + - taux: 45% + +grille: + description: >- + C'est un barème sous la forme d'une grille de correspondance. C'est le + mécanisme de calcul de l'impôt neutre, aussi appelé impôt non personnalisé. + + Il est composé de tranches qui se suivent. Il suffit de trouver l'assiette + qui correspond à la tranche, et de selectionner le montant associé à + l'assiette. + exemples: + grille avec multiplicateur et unité: >- + SMIC horaire: + formule: 10 €/heure + + revenu moyen: + formule: + 2000 €/mois + + trimestres validés: + formule: + grille: + unité: trimestres validés/an + assiette: revenu moyen + multiplicateur: SMIC horaire + tranches: + - montant: 0 + plafond: 150 heures/an + - montant: 1 + plafond: 300 heures/an + - montant: 2 + plafond: 450 heures/an + - montant: 3 + plafond: 600 heures/an + - montant: 4 + +taux progressif: + description: >- + Ce mécanisme permet de calculer un taux progressif. On spécifie pour chaque + tranche le plafond et le taux associé. Le taux effectif renvoyé est calculé + en lissant la différence de taux entre la borne inférieure et supérieure de + l'assiette + + > Par exemple, si nous nous avons les tranches suivantes : + + - taux: 50% / plafond: 0 + - taux: 100% / plafond: 1000 + + > Pour une assiette de 500, le taux retourné sera 75%, car il correspond au + > taux situé à la moitié de la tranche correspondante. + exemples: + base: >- + chiffre d'affaires: + formule: 30000 €/an + + plafond: + formule: 3000 €/mois + + taux réduction de cotisation: + formule: + taux progressif: + assiette: chiffre d'affaires + multiplicateur: plafond + tranches: + - taux: 100% + plafond: 75% + - taux: 0% + plafond: 100% +composantes: + description: >- + Beaucoup de cotisations sont composées de deux parties qui partagent la + méthode de calcul mais diffèrent par des paramètres différents. + + Pour ne pas définir deux variables presque redondantes, on utilise le + mécanisme de composante. Il se comportera comme une somme dans les calculs, + mais son affichage sur les pages /règle sera adapté. + + Il est même possible, pour les mécanismes `barème` et `produit` de garder en + commun un paramètre comme l'assiette, puis de déclarer des composantes pour + le taux. + + > L'exemple le plus courant de composantes, c'est la distinction part + employeur, part salarié (ex. retraite AGIRC). + +allègement: + description: >- + Permet de réduire le montant d'une variable. + Très utilisé dans le contexte des impôts. + +encadrement: + description: Permet d'ajouter un plafond et/ou un plancher à une valeur. + exemples: + base: >- + assiette plafonnée: + formule: + encadrement: + plancher: 0 € + valeur: 2000 € + plafond: 1500 € + +durée: + description: Permet d'obtenir le nombre de jours entre deux dates + exemples: + base: >- + date d'embauche: 14/04/2008 + + ancienneté en fin d'année: + unité: an + formule: + durée: + depuis: date d'embauche + jusqu'à: 31/12/2020 + +synchronisation: + description: Pour éviter trop de saisies à l'utilisateur, certaines informations sont + récupérées à partir de ce que l'on appelle des API. Ce sont des services + auxquels ont fait appel pour obtenir des informations sur un sujet précis. + Par exemple, l'État français fournit gratuitement l'API géo, qui permet à + partir du nom d'une ville, d'obtenir son code postal, son département, la + population etc. + + Ce mécanismes `synchronisation` permet de faire le lien entre les règles de + notre système et les réponses de ces API. + +inversion numérique: + description: >- + La formule de calcul de cette variable n'est pas connue, souvent elle n'a + même pas de sens. Mais le mécanisme `inversion` indique qu'elle peut être + _estimée_ à partir de l'un des _objectifs_ listés sous l'attribut `avec`. Il + faut alors renseigner une valeur cible pour cet objectif. + + Voilà comment ça marche : on va donner à la variable une valeur au hasard, + calculer _l'objectif_, puis grâce à des calculs savants améliorer notre + choix jusqu'à ce que l'écart entre le calcul et la valeur cible devienne + satisfaisant. + + Concrètement, si l'on demande au moteur (même indirectement) la valeur d'une + variable qui a pour formule une inversion, il va vérifier qu'une des + possibilités d'inversion a bien une valeur calculée ou saisie, et procéder à + l'inversion décrite plus haut à partir de celle-ci. Sinon, ces possibilités + d'inversions seront listées comme manquantes. diff --git a/source/engine/mecanisms/barème.ts b/source/engine/mecanisms/barème.ts index 663f3b269..706c77cf4 100644 --- a/source/engine/mecanisms/barème.ts +++ b/source/engine/mecanisms/barème.ts @@ -20,7 +20,7 @@ export default function parse(parse, k, v) { // Barème en taux marginaux. if (v.composantes) { - //mécanisme de composantes. Voir known-mecanisms.md/composantes + //mécanisme de composantes. Voir mécanismes.md/composantes return decompose(parse, k, v) } if (v.variations) { diff --git a/source/engine/mecanisms/grille.ts b/source/engine/mecanisms/grille.ts index 3c1fbfd4e..38c246ca1 100644 --- a/source/engine/mecanisms/grille.ts +++ b/source/engine/mecanisms/grille.ts @@ -17,7 +17,7 @@ import { export default function parse(parse, k, v) { if (v.composantes) { - //mécanisme de composantes. Voir known-mecanisms.md/composantes + //mécanisme de composantes. Voir mécanismes.md/composantes return decompose(parse, k, v) } if (v.variations) { diff --git a/source/engine/mecanisms/régularisation.ts b/source/engine/mecanisms/régularisation.ts index 8d9e74b86..c1d78a295 100644 --- a/source/engine/mecanisms/régularisation.ts +++ b/source/engine/mecanisms/régularisation.ts @@ -6,6 +6,7 @@ import { Evaluation, groupByYear, liftTemporal2, + pureTemporal, Temporal, temporalAverage, temporalCumul @@ -95,17 +96,17 @@ function evaluate( const cumulatedVariables = node.explanation.variables.reduce( (acc, parsedVariable) => { const evaluation = evaluate(parsedVariable) - if (!evaluation.temporalValue) { + if (!evaluation.unit.denominators.some(unit => unit === 'mois')) { evaluationError( cache._meta.contextRule, - `Dans le mécanisme régularisation, la valeur annuelle ${parsedVariable.name} n'est pas une variables temporelle` + `Dans le mécanisme régularisation, la valeur cumulée '${parsedVariable.name}' n'est pas une variable numérique définie sur le mois` ) } return { ...acc, [parsedVariable.dottedName]: getMonthlyCumulatedValuesOverYear( currentYear, - evaluation.temporalValue, + evaluation.temporalValue ?? pureTemporal(evaluation.nodeValue), evaluation.unit ) } diff --git a/source/engine/mecanisms/tauxProgressif.ts b/source/engine/mecanisms/tauxProgressif.ts index 475aa0aff..2aebb2924 100644 --- a/source/engine/mecanisms/tauxProgressif.ts +++ b/source/engine/mecanisms/tauxProgressif.ts @@ -11,7 +11,7 @@ import { export default function parse(parse, k, v) { if (v.composantes) { - //mécanisme de composantes. Voir known-mecanisms.md/composantes + //mécanisme de composantes. Voir mécanismes.md/composantes return decompose(parse, k, v) } if (v.variations) { diff --git a/source/sites/publi.codes/App.js b/source/sites/publi.codes/App.js index 858bf0bd2..566d96da1 100644 --- a/source/sites/publi.codes/App.js +++ b/source/sites/publi.codes/App.js @@ -8,6 +8,7 @@ import { getSessionStorage } from '../../utils' import redirects from '../mon-entreprise.fr/redirects' import Landing from './Landing' import Studio from './LazyStudio' +import Mécanismes from './Mécanismes' function Router({ language }) { useEffect(() => { @@ -38,7 +39,8 @@ let RouterSwitch = () => { <> - + + diff --git a/source/sites/publi.codes/Header.js b/source/sites/publi.codes/Header.js index 02c2c9e16..affa0f307 100644 --- a/source/sites/publi.codes/Header.js +++ b/source/sites/publi.codes/Header.js @@ -1,6 +1,6 @@ import React from 'react' import { Link } from 'react-router-dom' -export const Header = ({ noSubtitle, sectionName = '' }) => ( +export const Header = ({ noSubtitle = false, sectionName = '' }) => (

@@ -19,7 +19,7 @@ export const Header = ({ noSubtitle, sectionName = '' }) => ( {!noSubtitle && (

- Un nouveau langage pour les algorithmes d'intérêt public. + Le langage pour les algorithmes d'intérêt public.

)}

diff --git a/source/sites/publi.codes/Landing.js b/source/sites/publi.codes/Landing.js index 323d414dd..59708f9e7 100644 --- a/source/sites/publi.codes/Landing.js +++ b/source/sites/publi.codes/Landing.js @@ -1,9 +1,7 @@ -import exemple1 from '!!raw-loader!./exemples/bareme-ir.yaml' -import exemple2 from '!!raw-loader!./exemples/douche.yaml' -import ColoredYaml from 'Components/rule/ColoredYaml' +import { Markdown } from 'Components/utils/markdown' +import { ScrollToTop } from 'Components/utils/Scroll' +import publicodeReadme from 'Engine/README.md' import React, { useEffect } from 'react' -import emoji from 'react-easy-emoji' -import { Link } from 'react-router-dom' import { Header } from './Header' export default function Landing() { @@ -21,67 +19,12 @@ export default function Landing() { document.body.appendChild(css) }) return ( -
+
+
-

Pourquoi ?

-

- Certains algorithmes sont bien trop importants pour être maintenus dans - une boîte noire, souvent privée, que seuls les développeurs expérimentés - peuvent comprendre. -

-

- {' '} - C'est notamment le cas d'une bonne partie de la loi, qui spécifie en - français des règles... et charge à d'autres de les implémenter - librement. -

-

- La plateforme publicodes fusionne documentation et - implémentation en partant d'un code simple. Ajouter une règle de calcul, - c'est déployer sans effort sur le Web la page de documentation - correspondante, lisible par tout citoyen. -


-

Pour aller plus loin:

- -

Projets phares

-

- La sécurité sociale et les impôts -{' '} - mon-entreprise.fr -

-
- -
- - Lancer le calcul ⚡ - -

- En plus du site Web, Mon-entreprise est disponible comme une{' '} - - bibliothèque de calcul autonome - - . -

-

- L'impact climatique de nos gestes du quotidien -   - futur.eco -

-
- -
- - Lancer le calcul ⚡ -
+
) } diff --git a/source/sites/publi.codes/Mécanismes.tsx b/source/sites/publi.codes/Mécanismes.tsx new file mode 100644 index 000000000..65db9d5ca --- /dev/null +++ b/source/sites/publi.codes/Mécanismes.tsx @@ -0,0 +1,65 @@ +import { Markdown } from 'Components/utils/markdown' +import { ScrollToTop } from 'Components/utils/Scroll' +import mecanisms from 'Engine/mecanisms.yaml' +import React, { useEffect } from 'react' +import { useLocation } from 'react-router' +import { HashLink as Link } from 'react-router-hash-link' +import { capitalise0 } from '../../utils' +import { Header } from './Header' + +type MecanismProp = { + exemples: { base: string } + description: string + name: string +} +const Mecanism = ({ name, description, exemples }: MecanismProp) => ( + +

+
{name}
+

+ + {exemples && ( + <> + {Object.entries(exemples).map(([name, exemple]) => ( + +

{name === 'base' ? 'Exemple' : capitalise0(name)}

+ +
+ ))}{' '} + + )} + Retour à la liste +
+) +export default function Landing() { + useEffect(() => { + var css = document.createElement('style') + css.type = 'text/css' + css.innerHTML = ` + #js { + animation: appear 0.5s; + opacity: 1; + } + #loading { + display: none !important; + }` + document.body.appendChild(css) + }) + return ( +
+ +
+

Mécanismes existants

+
    + {Object.keys(mecanisms).map(name => ( +
  • + {name} +
  • + ))} +
+ {Object.entries(mecanisms).map(([name, data]) => ( + + ))} +
+ ) +} diff --git a/source/sites/publi.codes/Studio.tsx b/source/sites/publi.codes/Studio.tsx index 34e594585..5ddaa27a6 100644 --- a/source/sites/publi.codes/Studio.tsx +++ b/source/sites/publi.codes/Studio.tsx @@ -1,5 +1,3 @@ -import baremeIr from '!!raw-loader!./exemples/bareme-ir.yaml' -import douche from '!!raw-loader!./exemples/douche.yaml' import { ControlledEditor } from '@monaco-editor/react' import Engine from 'Engine/react' import { safeLoad } from 'js-yaml' @@ -10,11 +8,6 @@ import { useLocation } from 'react-router' import styled from 'styled-components' import { Header } from './Header' -let examples = { - 'bareme-ir': baremeIr, - douche -} - let initialInput = `a: formule: 10€ b: @@ -30,15 +23,8 @@ d: export default function Studio() { const search = new URLSearchParams(useLocation().search ?? '') - const currentExample = search.get('exemple') const code = search.get('code') - const [editorValue, setEditorValue] = useState( - code - ? code - : currentExample && Object.keys(examples).includes(currentExample) - ? examples[currentExample] - : initialInput - ) + const [editorValue, setEditorValue] = useState(code ? code : initialInput) const [targets, setTargets] = useState([]) const [rules, setRules] = useState(editorValue) const handleShare = useCallback(() => { diff --git a/source/sites/publi.codes/exemples/bareme-ir.yaml b/source/sites/publi.codes/exemples/bareme-ir.yaml deleted file mode 100644 index 75ae4bc44..000000000 --- a/source/sites/publi.codes/exemples/bareme-ir.yaml +++ /dev/null @@ -1,23 +0,0 @@ -revenu imposable: - formule: 18000 € - -revenu abattu: - formule: - allègement: - assiette: revenu imposable - abattement: 10% - -impôt sur le revenu: - formule: - barème: - assiette: revenu abattu - tranches: - - taux: 0% - plafond: 9807€ - - taux: 14% - plafond: 27086€ - - taux: 30% - plafond: 72617€ - - taux: 41% - plafond: 153783€ - - taux: 45% diff --git a/source/sites/publi.codes/exemples/douche.yaml b/source/sites/publi.codes/exemples/douche.yaml deleted file mode 100644 index d002af0dc..000000000 --- a/source/sites/publi.codes/exemples/douche.yaml +++ /dev/null @@ -1,96 +0,0 @@ -douche: - icônes: 🚿 - -douche . impact par douche: - titre: Une douche - formule: impact par litre * litres d'eau - -douche . impact par litre: - formule: eau . impact par litre froid + chauffage . impact par litre - -douche . litres d'eau: - formule: durée de la douche * litres par minute - -douche . litres par minute: - unité: l/minute - formule: - variations: - - si: pomme de douche économe - alors: 9 - - sinon: 18 - références: - - https://www.jeconomiseleau.org/index.php/particuliers/economies-par-usage/la-douche-et-le-bain - -douche . pomme de douche économe: - question: Utilisez-vous une pomme de douche économe ? - par défaut: non - -douche . durée de la douche: - question: Combien de temps dure votre douche en général (en minutes) ? - par défaut: 5 - unité: minute - suggestions: - expresse: 5 - moyenne: 10 - lente: 20 - -chauffage: - icônes: 🔥 - -chauffage . type: - question: Votre eau est chauffée comment ? - formule: - une possibilité: - choix obligatoire: oui - possibilités: - - gaz - - fioul - - électricité - par défaut: gaz - -chauffage . impact par kWh: - unité: kgCO₂e/kWh - formule: - variations: - - si: type = 'gaz' - alors: 0.227 - - si: type = 'fioul' - alors: 0.324 - - si: type = 'électricité' - alors: 0.059 - - note: | - La base carbone de l'ADEME ne permet malheureusement pas de faire des liens profonds vers les chiffres utilisés. - Pour l'électricité, nous retenons le chiffre de l'ADEME "Electricité - 2016 - usage : Eau Chaude Sanitaire - consommation". - références: - - http://www.bilans-ges.ademe.fr/fr/accueil - - https://www.electricitymap.org/?page=country&solar=false&remote=true&wind=false&countryCode=FR - - https://decrypterlenergie.org/decryptage-quel-est-le-contenu-en-co2-du-kwh-electrique - -chauffage . énergie consommée par litre: - formule: 0.0325 - unité: kWh - références: - - https://www.econologie.com/forums/plomberie-et-sanitaire/prix-reel-d-un-bain-ou-d-une-douche-pour-l-eau-et-chauffage-t12727.html - -chauffage . impact par litre: - titre: impact par litre chauffé - formule: impact par kWh * énergie consommée par litre - -eau: - icônes: 🌊 - -eau . impact par litre froid: - unité: kgCO₂e/l - formule: eau potable + traitement eau usée - références: - - >- - http://www.bilans-ges.ademe.fr/documentation/UPLOAD_DOC_FR/index.htm?boissons.htm - -eau . impact par litre froid . eau potable: - unité: kgCO₂e/l - formule: 0.000132 - -eau . impact par litre froid . traitement eau usée: - unité: kgCO₂e/l - formule: 0.000262 diff --git a/source/webpack.common.js b/source/webpack.common.js index 0f182c584..3ccda1226 100644 --- a/source/webpack.common.js +++ b/source/webpack.common.js @@ -134,6 +134,10 @@ module.exports.commonLoaders = ({ legacy = false } = {}) => { { test: /\.ne$/, use: [babelLoader, 'nearley-loader'] + }, + { + test: /\.md$/, + use: ['raw-loader'] } ] } diff --git a/yarn.lock b/yarn.lock index e41f2a28c..8d9d6c3e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9365,6 +9365,13 @@ react-router-dom@^5.1.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-router-hash-link@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/react-router-hash-link/-/react-router-hash-link-1.2.2.tgz#7a0ad5e925d49596d19554de8bc6c554ce4f8099" + integrity sha512-LBthLVHdqPeKDVt3+cFRhy15Z7veikOvdKRZRfyBR2vjqIE7rxn+tKLjb6DOmLm6JpoQVemVDnxQ35RVnEHdQA== + dependencies: + prop-types "^15.6.0" + react-router@5.1.2, react-router@^5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"