🔥 Supprime UNSAFE_evaluateRule

pull/1312/head
Johan Girod 2020-12-17 10:30:52 +01:00
parent adcbd330bd
commit 3a8c201d44
24 changed files with 230 additions and 233 deletions

View File

@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strict": true
},
"include": ["build.js"]
}

View File

@ -1,7 +1,7 @@
import { hideNotification } from 'Actions/actions'
import animate from 'Components/ui/animate'
import { useEngine, useInversionFail } from 'Components/utils/EngineContext'
import Engine, { EvaluatedRule } from 'publicodes'
import Engine, { RuleNode } from 'publicodes'
import emoji from 'react-easy-emoji'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
@ -14,8 +14,9 @@ import { ScrollToElement } from './utils/Scroll'
// with the "type: notification" attribute. The display can be customized with
// the "sévérité" attribute. The notification will only be displayed if the
// publicode rule is applicable.
type Notification = Pick<EvaluatedRule, 'dottedName' | 'description'> & {
type: 'notification'
type Notification = {
dottedName: RuleNode['dottedName']
description: RuleNode['rawNode']['description']
sévérité: 'avertissement' | 'information'
}
@ -26,12 +27,15 @@ export function getNotifications(engine: Engine) {
rule.rawNode['type'] === 'notification' &&
!!engine.evaluate(rule.dottedName).nodeValue
)
.map((node) => node.dottedName)
.map(({ dottedName, rawNode: { sévérité, description } }) => ({
dottedName,
sévérité,
description,
}))
}
export default function Notifications() {
const { t } = useTranslation()
const engine = useEngine()
const inversionFail = useInversionFail()
const hiddenNotifications = useSelector(
(state: RootState) => state.simulation?.hiddenNotifications
@ -46,11 +50,10 @@ export default function Notifications() {
'simulateurs.inversionFail',
'Le montant saisi abouti à un résultat impossible. Cela est dû à un effet de seuil dans le calcul des cotisations.\n\nNous vous invitons à réessayer en modifiant légèrement le montant renseigné (quelques euros de plus par exemple).'
),
type: 'notification',
sévérité: 'avertissement',
},
]
: ((getNotifications(engine) as any) as Array<Notification>)
: (getNotifications(engine) as Array<Notification>)
if (!messages?.length) return null
return (

View File

@ -13,6 +13,7 @@ import { Trans, useTranslation } from 'react-i18next'
import { DottedName } from 'modele-social'
import './PaySlip.css'
import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections'
import { RuleNode } from 'publicodes/dist/types/rule'
export const SECTION_ORDER = [
'protection sociale . santé',
@ -27,7 +28,7 @@ export const SECTION_ORDER = [
type Section = typeof SECTION_ORDER[number]
function getSection(rule: ASTNode & { nodeKind: 'rule' }): Section {
function getSection(rule: RuleNode): Section {
const section = ('protection sociale . ' +
rule.rawNode.cotisation?.branche) as Section
if (SECTION_ORDER.includes(section)) {

View File

@ -1,14 +1,13 @@
import RuleLink from 'Components/RuleLink'
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
import { EvaluatedNode, EvaluatedRule } from 'publicodes'
import React, { useContext } from 'react'
import { animated, useSpring } from 'react-spring'
import { DottedName } from 'modele-social'
import styled from 'styled-components'
import { EngineContext, useEngine } from './utils/EngineContext'
import { useSelector } from 'react-redux'
import { targetUnitSelector } from 'Selectors/simulationSelectors'
import { Names } from 'modele-social/dist/names'
import { EvaluatedNode } from 'publicodes'
import React from 'react'
import { useSelector } from 'react-redux'
import { animated, useSpring } from 'react-spring'
import { targetUnitSelector } from 'Selectors/simulationSelectors'
import styled from 'styled-components'
import { useEngine } from './utils/EngineContext'
const BarStack = styled.div`
display: flex;
@ -150,7 +149,7 @@ export default function StackedRulesChart({ data }: StackedRulesChartProps) {
key: dottedName,
value: engine.evaluate({ valeur: dottedName, unité: targetUnit })
.nodeValue,
legend: <RuleLink dottedName={dottedName} children={title} />,
legend: <RuleLink dottedName={dottedName}>{title}</RuleLink>,
color,
}))}
/>

View File

@ -14,7 +14,13 @@ import {
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import { DottedName } from 'modele-social'
import { Names } from 'modele-social/dist/names'
import { ASTNode, EvaluatedRule, formatValue, reduceAST } from 'publicodes'
import {
ASTNode,
EvaluatedNode,
formatValue,
reduceAST,
RuleNode,
} from 'publicodes'
import { isNil } from 'ramda'
import { Fragment, useCallback, useContext } from 'react'
import emoji from 'react-easy-emoji'
@ -80,6 +86,10 @@ export default function TargetSelection({ showPeriodSwitch = true }) {
type TargetProps = {
dottedName: DottedName
}
type TargetType = EvaluatedNode &
RuleNode['rawNode'] &
RuleNode & { dottedName: DottedName }
const Target = ({ dottedName }: TargetProps) => {
const activeInput = useSelector((state: RootState) => state.activeTargetInput)
const engine = useEngine()
@ -89,7 +99,7 @@ const Target = ({ dottedName }: TargetProps) => {
unité: useSelector(targetUnitSelector),
arrondi: 'oui',
})
const target = { ...evaluation, ...rule.rawNode, ...rule }
const target: TargetType = { ...evaluation, ...rule.rawNode, ...rule }
const dispatch = useDispatch()
const onSuggestionClick = useCallback(
(value) => {
@ -155,7 +165,7 @@ const Target = ({ dottedName }: TargetProps) => {
)
}
const Header = ({ target }: { target: EvaluatedRule<DottedName> }) => {
const Header = ({ target }: { target: TargetType }) => {
const sitePaths = useContext(SitePathsContext)
const { t } = useTranslation()
const { pathname } = useLocation()
@ -179,7 +189,7 @@ const Header = ({ target }: { target: EvaluatedRule<DottedName> }) => {
}
type TargetInputOrValueProps = {
target: EvaluatedRule<DottedName>
target: TargetType
isActiveInput: boolean
isSmallTarget: boolean
}
@ -290,7 +300,6 @@ function TitreRestaurant() {
}
function AidesGlimpse() {
const targetUnit = useSelector(targetUnitSelector)
const { language } = useTranslation().i18n
const dottedName = 'contrat salarié . aides employeur' as Names
const engine = useEngine()
const aides = engine.getRule(dottedName)

View File

@ -2,7 +2,7 @@ import {
InputCommonProps,
RuleInputProps,
} from 'Components/conversation/RuleInput'
import { EvaluatedRule } from 'publicodes'
import { RuleNode } from 'publicodes'
import { useCallback, useMemo } from 'react'
import styled from 'styled-components'
import InputSuggestions from './InputSuggestions'
@ -12,7 +12,7 @@ type DateInputProps = {
id: InputCommonProps['id']
onSubmit: RuleInputProps['onSubmit']
value: InputCommonProps['value']
suggestions: EvaluatedRule['suggestions']
suggestions: RuleNode['suggestions']
}
export default function DateInput({

View File

@ -1,6 +1,6 @@
import classnames from 'classnames'
import { Markdown } from 'Components/utils/markdown'
import { ASTNode } from 'publicodes'
import { RuleNode } from 'publicodes'
import { References } from 'publicodes-react'
import { Rule } from 'publicodes/dist/types/rule'
import { useCallback, useEffect, useState } from 'react'
@ -25,7 +25,7 @@ import { binaryQuestion, InputCommonProps, RuleInputProps } from './RuleInput'
*/
export type Choice = ASTNode & { nodeKind: 'rule' } & {
export type Choice = RuleNode & {
canGiveUp?: boolean
children: Array<Choice>
}

View File

@ -7,13 +7,9 @@ import PercentageField from 'Components/PercentageField'
import ToggleSwitch from 'Components/ui/ToggleSwitch'
import { EngineContext } from 'Components/utils/EngineContext'
import { DottedName } from 'modele-social'
import Engine, {
ASTNode,
EvaluatedRule,
formatValue,
reduceAST,
} from 'publicodes'
import Engine, { ASTNode, formatValue, reduceAST } from 'publicodes'
import { Evaluation } from 'publicodes/dist/types/AST/types'
import { RuleNode } from 'publicodes/dist/types/rule'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import DateInput from './DateInput'
@ -37,7 +33,8 @@ export type InputCommonProps<Name extends string = string> = Pick<
RuleInputProps<Name>,
'dottedName' | 'onChange' | 'autoFocus' | 'className'
> &
Pick<EvaluatedRule<Name>, 'title' | 'question' | 'suggestions'> & {
Pick<RuleNode, 'title' | 'suggestions'> & {
question: RuleNode['rawNode']['question']
key: string
id: string
value: any //TODO EvaluatedRule['nodeValue']
@ -181,7 +178,7 @@ export default function RuleInput<Name extends string = DottedName>({
)
}
const getVariant = (node: ASTNode & { nodeKind: 'rule' }) =>
const getVariant = (node: RuleNode) =>
reduceAST<false | (ASTNode & { nodeKind: 'une possibilité' })>(
(_, node) => {
if (node.nodeKind === 'une possibilité') {

View File

@ -46,7 +46,7 @@ export default function SelectEuropeCountry({
name="country"
id={id}
className="ui__"
defaultValue={value?.slice(1, -1)}
defaultValue={value ? value.slice(1, -1) : undefined}
onChange={(e) => onChange(`'${e.target.value}'`)}
>
<option disabled selected hidden></option>

View File

@ -11,13 +11,13 @@ import { EngineContext, useEngine } from 'Components/utils/EngineContext'
import { ScrollToTop } from 'Components/utils/Scroll'
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
import { useNextQuestions } from 'Components/utils/useNextQuestion'
import { EvaluatedRule, UNSAFE_evaluateRule, formatValue } from 'publicodes'
import { Fragment, useCallback, useContext, useEffect, useState } from 'react'
import { DottedName } from 'modele-social'
import { RuleNode } from 'publicodes'
import { Fragment, useCallback, useContext, useEffect } from 'react'
import emoji from 'react-easy-emoji'
import { Trans } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from 'Reducers/rootReducer'
import { DottedName } from 'modele-social'
import { situationSelector } from 'Selectors/simulationSelectors'
import styled from 'styled-components'
import { CompanySection } from '../Home'
@ -338,8 +338,8 @@ function SubSection({
type SimpleFieldProps = {
dottedName: DottedName
summary?: EvaluatedRule['résumé']
question?: EvaluatedRule['question']
summary?: RuleNode['rawNode']['résumé']
question?: RuleNode['rawNode']['question']
}
function SimpleField({ dottedName, question, summary }: SimpleFieldProps) {
const dispatch = useDispatch()

View File

@ -2,11 +2,13 @@ import { BlobProvider } from '@react-pdf/renderer'
import Overlay from 'Components/Overlay'
import Checkbox from 'Components/ui/Checkbox'
import { ThemeColorsContext } from 'Components/utils/colors'
import { EngineContext, EngineProvider } from 'Components/utils/EngineContext'
import { TrackerContext } from 'Components/utils/withTracker'
import { RuleNode } from 'publicodes/dist/types/rule'
import { lazy, Suspense, useContext, useRef, useState } from 'react'
import emoji from 'react-easy-emoji'
import SignaturePad from 'react-signature-pad-wrapper'
import PDFDocument, { PDFDocumentProps } from './PDFDocument'
import PDFDocument from './PDFDocument'
const IS_TOUCH_DEVICE = isOnTouchDevice()
type SignaturePadInstance = {
@ -15,7 +17,7 @@ type SignaturePadInstance = {
}
type EndBlockProps = {
fields: PDFDocumentProps['fields']
fields: Array<RuleNode>
isMissingValues: boolean
}
@ -23,7 +25,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
const [isCertified, setCertified] = useState(false)
const [place, setPlace] = useState<string>()
const [showDownloadLink, toggleDownloadLink] = useState(false)
const engine = useContext(EngineContext)
const { darkColor } = useContext(ThemeColorsContext)
const signatureRef = useRef<SignaturePadInstance>()
const tracker = useContext(TrackerContext)
@ -131,13 +133,15 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
>
<LazyBlobProvider
document={
<PDFDocument
fields={fields}
signatureURL={
IS_TOUCH_DEVICE && signatureRef.current?.toDataURL()
}
place={place}
/>
<EngineProvider value={engine}>
<PDFDocument
fields={fields}
signatureURL={
IS_TOUCH_DEVICE && signatureRef.current?.toDataURL()
}
place={place}
/>
</EngineProvider>
}
>
{({ url, loading, error }) =>
@ -175,7 +179,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) {
])
}
className="ui__ cta plain button"
download="demande-mobilité-europe.pdf"
download="demande-mobilité-internationale.pdf"
>
Télécharger le fichier
</a>

View File

@ -1,47 +1,51 @@
import { StyleSheet, Text, View } from '@react-pdf/renderer'
import { formatValue, EvaluatedRule } from 'publicodes'
import Value from 'Components/EngineValue'
import { EngineContext } from 'Components/utils/EngineContext'
import { formatValue } from 'publicodes'
import { RuleNode } from 'publicodes/dist/types/rule'
import { useContext } from 'react'
export type FieldsPDFProps = {
fields: Array<EvaluatedRule<string>>
type FieldsPDFProps = {
fields: Array<RuleNode>
}
export default function FieldsPDF({ fields }: FieldsPDFProps) {
const engine = useContext(EngineContext)
return (
<>
{fields.map((field) => (
<View style={styles.field} key={field.dottedName} wrap={false}>
{field.type === 'groupe' ? (
<>
<Text style={styles.subtitle}>
{field.title}{' '}
{field.note && (
<Text style={styles.fieldNumber}>({field.note})</Text>
)}
</Text>
</>
) : (
<>
<Text style={styles.name}>
{field.question ?? field.title}{' '}
{field.note && (
<Text style={styles.fieldNumber}>({field.note})</Text>
)}
</Text>
{field.nodeValue != null && (
<Text style={styles.value}>
{formatValue(field) +
(field.API === 'commune'
? ` (${
(field.nodeValue as Record<string, unknown>)
.codePostal as string
})`
: '')}
{fields.map(
({ rawNode: { type, question, note, API }, title, dottedName }) => (
<View style={styles.field} key={dottedName} wrap={false}>
{type === 'groupe' ? (
<>
<Text style={styles.subtitle}>
{title}{' '}
{note && <Text style={styles.fieldNumber}>({note})</Text>}
</Text>
)}
</>
)}
</View>
))}
</>
) : (
<>
<Text style={styles.name}>
{question ?? title}{' '}
{note && <Text style={styles.fieldNumber}>({note})</Text>}
</Text>
<Text style={styles.value}>
{formatValue(engine.evaluate(dottedName)) +
(API === 'commune'
? ` (${
(engine.evaluate(dottedName).nodeValue as Record<
string,
unknown
>)?.codePostal as string
})`
: '')}{' '}
</Text>
</>
)}
</View>
)
)}
</>
)
}

View File

@ -2,19 +2,20 @@ import ReactPDF, {
Document,
Font,
Image,
Link,
Page,
StyleSheet,
Text,
View,
Link,
} from '@react-pdf/renderer'
import urssafPng from 'Images/destinataires/URSSAF.png'
import FieldsPDF, { FieldsPDFProps, styles as fieldStyles } from './FieldsPDF'
import { RuleNode } from 'publicodes'
import FieldsPDF, { styles as fieldStyles } from './FieldsPDF'
import montserratUrl from './Montserrat-SemiBold.ttf'
import robotoUrl from './Roboto-Regular.ttf'
export type PDFDocumentProps = {
fields: FieldsPDFProps['fields']
fields: Array<RuleNode>
signatureURL?: ReactPDF.SourceObject | false
place?: string
}
@ -33,8 +34,8 @@ export default function PDFDocument({
<View>
<Text style={styles.title}>
{fields.find(({ dottedName }) => dottedName === 'détachement')
? 'Demande de détachement en Europe'
: "Demande d'activité transfrontalière simultanée en Europe"}
? 'Demande de détachement'
: "Demande d'activité transfrontalière"}
</Text>
<Text style={styles.texte}>
Afin dexaminer votre situation au regard des règlements

View File

@ -139,9 +139,9 @@ demande . détachement possible:
détachement:
note: 3.3
applicable si: demande . détachement possible
titre: Demande de détachement
formule: demande . détachement possible
par défaut: non
formule: oui
type: groupe
détachement . pays:
@ -195,10 +195,11 @@ détachement . activité . code postal:
type: texte
activité transfrontalière simultanée:
formule: demande . détachement possible = non
par défaut: non
non applicable si: demande . détachement possible
formule: oui
titre: Demande d'activité transfrontalière simultanée
type: groupe
activité transfrontalière simultanée . salarié hors France:
question: >
Travaillez-vous en tant que salarié dans un autre pays ?

View File

@ -1,19 +1,20 @@
import { Explicable } from 'Components/conversation/Explicable'
import RuleInput from 'Components/conversation/RuleInput'
import { Condition } from 'Components/EngineValue'
import * as Animate from 'Components/ui/animate'
import Emoji from 'Components/utils/Emoji'
import { EngineContext, EngineProvider } from 'Components/utils/EngineContext'
import { Markdown } from 'Components/utils/markdown'
import { usePersistingState } from 'Components/utils/persistState'
import Engine, { UNSAFE_evaluateRule, EvaluatedRule } from 'publicodes'
import { equals } from 'ramda'
import Engine, { UNSAFE_isNotApplicable } from 'publicodes'
import { equals, isEmpty } from 'ramda'
import {
lazy,
createElement,
lazy,
Suspense,
useCallback,
useState,
useContext,
useState,
} from 'react'
import emoji from 'react-easy-emoji'
import { hash } from '../../../../utils'
@ -91,21 +92,24 @@ export default function FormulaireMobilitéIndépendant() {
const useFields = (
engine: Engine<string>,
fieldNames: Array<string>,
situation: Record<string, unknown>
): Array<EvaluatedRule> => {
const fields = fieldNames
.map((name) => UNSAFE_evaluateRule(engine, name))
.filter(
(node: EvaluatedRule) =>
node.isNotApplicable !== true &&
// TODO change this when not applicable value can be differenciated from false value
(equals(node.missingVariables, { [node.dottedName]: 1 }) ||
node.dottedName in situation ||
(node.nodeValue !== false && node.nodeValue !== null)) &&
(node.question || ((node.type || node.API) && node.nodeValue !== false))
)
return fields
fieldNames: Array<string>
): Array<ReturnType<Engine['getRule']>> => {
return fieldNames
.filter((dottedName) => {
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
const evaluation = engine.evaluate(dottedName)
const rule = engine.getRule(dottedName)
return (
isNotApplicable === false &&
(equals(evaluation.missingVariables, { [dottedName]: 1 }) ||
isEmpty(evaluation.missingVariables)) &&
(rule.rawNode.question ||
rule.rawNode.API ||
rule.rawNode.type ||
rule.rawNode.description)
)
})
.map((dottedName) => engine.getRule(dottedName))
}
const VERSION = hash(JSON.stringify(formulaire))
@ -133,64 +137,66 @@ function FormulairePublicodes() {
}, [clearFieldsKey, setSituation])
engine.setSituation(situation)
const fields = useFields(engine, Object.keys(formulaire), situation)
const missingValues = fields.filter(
({ dottedName, type }) =>
type !== 'groupe' &&
(situation[dottedName] == null || situation[dottedName] === '')
const fields = useFields(engine, Object.keys(formulaire))
const isMissingValues = fields.some(
({ dottedName }) => !isEmpty(engine.evaluate(dottedName).missingVariables)
)
const isMissingValues = !!missingValues.length
return (
<Animate.fromTop key={clearFieldsKey}>
{fields.map((field) => (
<Animate.fromTop key={field.dottedName}>
{field.type === 'groupe' ? (
<>
{createElement(
`h${Math.min(field.dottedName.split(' . ').length + 1, 6)}`,
{},
field.title
)}
{field.description && <Markdown source={field.description} />}
</>
) : field.type === 'notification' && field.nodeValue === true ? (
<small
css={`
color: #ff2d96;
`}
>
{field.description}
</small>
) : (
<>
<label htmlFor={field.dottedName}>
{field.question ? (
<span
css={`
margin-top: 0.6rem;
`}
>
{field.question}
</span>
) : (
<small>{field.title}</small>
)}{' '}
</label>
{field.description && (
<Explicable>
<h3>{field.title}</h3>
<Markdown source={field.description} />
</Explicable>
)}
<RuleInput
id={field.dottedName}
dottedName={field.dottedName}
onChange={(value) => onChange(field.dottedName, value)}
/>
</>
)}
</Animate.fromTop>
))}
{fields.map(
({ rawNode: { description, type, question }, title, dottedName }) => (
<Animate.fromTop key={dottedName}>
{type === 'groupe' ? (
<>
{createElement(
`h${Math.min(dottedName.split(' . ').length + 1, 6)}`,
{},
title
)}
{description && <Markdown source={description} />}
</>
) : type === 'notification' ? (
<Condition expression={dottedName}>
<small
css={`
color: #ff2d96;
`}
>
{description}
</small>
</Condition>
) : (
<>
<label htmlFor={dottedName}>
{question ? (
<span
css={`
margin-top: 0.6rem;
`}
>
{question}
</span>
) : (
<small>{title}</small>
)}{' '}
</label>
{description && (
<Explicable>
<h3>{title}</h3>
<Markdown source={description} />
</Explicable>
)}
<RuleInput
id={dottedName}
dottedName={dottedName}
onChange={(value) => onChange(dottedName, value)}
/>
</>
)}
</Animate.fromTop>
)
)}
<Suspense fallback={null}>
<LazyEndBlock fields={fields} isMissingValues={isMissingValues} />

View File

@ -7,6 +7,7 @@
/* eslint-disable no-undef */
import rules from 'modele-social'
import Engine from 'publicodes'
import artisteAuteurConfig from '../../source/site/pages/Simulateurs/configs/artiste-auteur.yaml'
import autoentrepreneurConfig from '../../source/site/pages/Simulateurs/configs/auto-entrepreneur.yaml'
import independantConfig from '../../source/site/pages/Simulateurs/configs/indépendant.yaml'
@ -20,6 +21,7 @@ import independentSituations from './simulations-indépendant.yaml'
import professionsLibéralesSituations from './simulations-professions-libérales.yaml'
import remunerationDirigeantSituations from './simulations-rémunération-dirigeant.yaml'
import employeeSituations from './simulations-salarié.yaml'
import aideDéclarationIndépendantsSituations from './aide-déclaration-indépendants.yaml'
const roundResult = (arr) => arr.map((x) => Math.round(x))
const engine = new Engine(rules)

View File

@ -115,9 +115,9 @@
"prepare": "if [ -z \"$NETLIFY\" ]; then yarn workspaces run prepare; fi",
"lint": "yarn lint:eslintrc && yarn lint:eslint && yarn lint:prettier",
"test": "yarn workspaces run test",
"test:type": "yarn workspace publicodes run tsc && yarn workspace publicodes-react run tsc && yarn workspace mon-entreprise run tsc",
"test:type": "yarn workspaces run tsc",
"test:regressions": "jest",
"clean": "yarn workspaces run clean",
"clean": "yarn workspaces run clean && rimraf node_modules",
"start": "yarn workspace publicodes build --watch & yarn workspace publicodes-react build --watch & yarn workspace mon-entreprise start",
"moso:up": "yarn workspace modele-social run up && yarn workspace mon-entreprise upgrade modele-social",
"publicodes:up": "yarn workspace publicodes-react upgrade publicodes && yarn workspace mon-entreprise upgrade publicodes publicodes-react"

View File

@ -10,3 +10,4 @@ export const formatValue = publicodes.formatValue
export const utils = publicodes.utils
export const translateRules = publicodes.translateRules
export const UNSAFE_isNotApplicable = publicodes.UNSAFE_isNotApplicable
export const mecanismsDoc = publicodes.mecanismsDoc

View File

@ -1,9 +1,4 @@
<<<<<<< HEAD:publicodes/core/source/error.ts
const coerceArray = (x) => (Array.isArray(x) ? x : [x])
=======
import { Logger } from '.'
import { Context } from './parsePublicodes'
>>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/error.ts
export class EngineError extends Error {}
export function syntaxError(

View File

@ -3,7 +3,6 @@ import { compose, mapObjIndexed } from 'ramda'
import { reduceAST } from './AST'
import { ASTNode, EvaluatedNode, NodeKind } from './AST/types'
import { evaluationFunctions } from './evaluationFunctions'
import { simplifyNodeUnit } from './nodeUnits'
import parse from './parse'
import parsePublicodes, { disambiguateReference } from './parsePublicodes'
import {
@ -13,11 +12,6 @@ import {
} from './replacement'
import { Rule, RuleNode } from './rule'
import * as utils from './ruleUtils'
<<<<<<< HEAD:publicodes/core/source/index.ts
import { reduceAST } from './AST'
import mecanismsDoc from '../../docs/mecanisms.yaml'
=======
>>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/index.ts
const emptyCache = () => ({
_meta: { ruleStack: [] },
@ -47,16 +41,13 @@ export type EvaluationOptions = Partial<{
export * as cyclesLib from './AST/graph'
export { reduceAST, transformAST } from './AST/index'
export { Evaluation, Unit } from './AST/types'
export { formatValue, capitalise0 } from './format'
export { serializeUnit } from './units'
export { capitalise0, formatValue } from './format'
export { simplifyNodeUnit } from './nodeUnits'
export { default as translateRules } from './translateRules'
export { ASTNode, EvaluatedNode }
export { parsePublicodes }
export { mecanismsDoc }
export { utils }
export { Rule }
export { serializeUnit } from './units'
export { parsePublicodes, utils }
export { Rule, RuleNode, ASTNode, EvaluatedNode }
export { default as mecanismsDoc } from '../../docs/mecanisms.yaml'
type PublicodesExpression = string | Record<string, unknown> | number
export type Logger = {
@ -166,7 +157,7 @@ export default class Engine<Name extends string = string> {
disambiguateReference(this.parsedRules)
)(
parse(value, {
dottedName: `evaluation`,
dottedName: 'evaluation',
parsedRules: {},
logger: this.logger,
})
@ -189,7 +180,7 @@ export default class Engine<Name extends string = string> {
}
/**
This function allows to mimic the old 'isApplicable' property on evaluatedRules
This function allows to mimic the former 'isApplicable' property on evaluatedRules
It will be deprecated when applicability will be encoded as a Literal type
*/
@ -216,7 +207,10 @@ export function UNSAFE_isNotApplicable<DottedName extends string = string>(
return fn(engine.evaluate(rule))
}
if (node.nodeKind === 'applicable si') {
return (node.explanation.condition as any).nodeValue === false
return (
(node.explanation.condition as any).nodeValue === false ||
fn(node.explanation.valeur)
)
}
if (node.nodeKind === 'non applicable si') {
return (
@ -224,45 +218,14 @@ export function UNSAFE_isNotApplicable<DottedName extends string = string>(
(node.explanation.condition as any).nodeValue !== null
)
}
if (node.nodeKind === 'rule') {
return (
(node.explanation.parent as any).nodeValue === false ||
fn(node.explanation.valeur)
)
}
},
false,
engine.evaluate(dottedName)
)
}
/**
This function allows smother migration to the new Engine API
It will be deprecated when applicability will be encoded as a Literal type
Prefer the use of `engine.evaluate(engine.getRule(dottedName))`
*/
export function UNSAFE_evaluateRule<DottedName extends string = string>(
engine: Engine<DottedName>,
dottedName: DottedName,
modifiers: Object = {}
): EvaluatedRule<DottedName> {
const evaluation = simplifyNodeUnit(
engine.evaluate({ valeur: dottedName, ...modifiers })
)
const rule = engine.getRule(dottedName) as RuleNode & {
dottedName: DottedName
}
return {
isNotApplicable: UNSAFE_isNotApplicable(engine, dottedName),
...rule.rawNode,
...rule,
...evaluation,
} as EvaluatedRule<DottedName>
}
export type EvaluatedRule<Name extends string = string> = EvaluatedNode &
Omit<
(ASTNode & {
nodeKind: 'rule'
}) &
(ASTNode & {
nodeKind: 'rule'
})['rawNode'] & { dottedName: Name; isNotApplicable: boolean },
'nodeKind'
>

View File

@ -24,6 +24,7 @@ export type Rule = {
résumé?: string
icônes?: string
titre?: string
sévérité?: string
cotisation?: {
branche: string
}

View File

@ -6,6 +6,7 @@
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"clean": "rimraf dist node_modules",
"prepare": "yarn run build",
"test": "echo \"Error: no test specified\""
},

View File

@ -278,7 +278,7 @@ export function Leaf(
const { dottedName, nodeValue, unit } = node
const rule = engine?.getRule(node.dottedName)
if (!rule) {
throw new Error(`Unknown node`)
throw new Error('Unknown node')
}
const [folded, setFolded] = useState(true)

View File

@ -6,7 +6,8 @@
"license": "MIT",
"scripts": {
"prepare": "echo 1",
"test": "echo 1"
"test": "echo 1",
"clean": "rimraf node_modules"
},
"devDependencies": {
"core-js": "^3.8.1"