fig-tree-evaluator 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -776,14 +776,21 @@ Example using "data" passed in dynamically as part of expression:
|
|
|
776
776
|
----
|
|
777
777
|
### STRING_SUBSTITUTION
|
|
778
778
|
|
|
779
|
-
*Replace values in a string using simple parameter substitution*
|
|
779
|
+
*Replace values in a string using simple parameter (positional or named properties) substitution*
|
|
780
780
|
|
|
781
781
|
Aliases: `stringSubstitution`, `substitute`, `stringSub`, `replace`
|
|
782
782
|
|
|
783
783
|
#### Properties
|
|
784
784
|
|
|
785
785
|
- `string`<sup>*</sup>: (string) -- a parameterized (`%1`, `%2`) string, where the parameters are to be replaced by dynamic values. E.g. `"My name is %1 (age %2)"`
|
|
786
|
-
- `substitutions` (or `replacements`)<sup>*</sup>: (array) -- the values to be substituted into `string
|
|
786
|
+
- `substitutions` (or `replacements`, `values`)<sup>*</sup>: (array | object) -- the values to be substituted into `string`. Will be either an array or object depending on whether you're using positional replacements or named properties (see [below](#positional-replacement)).
|
|
787
|
+
- `trimWhiteSpace` (or `trimWhitespace`, `trim`): (boolean, default `true`) -- strips whitespace from the beginning or end of the substitution values
|
|
788
|
+
- `substitutionCharacter` (or `subCharacter`, `subChar`): (`"%"` or `"$"`) -- by default, when using positional replacement, it looks for the `%` token (i.e `%1, %2, etc`), but this can be changed to `$` (i.e. `$1, $2, $3, etc`) by setting this property to `$`.
|
|
789
|
+
- `numberMapping` (or `numMap`, `numberMap`, `pluralisation`, `pluralization`, `plurals`): (object) -- when replacing with named properties and you have replacement values that are numbers, it's possible to map values or ranges to specific string outputs. This can be used to produce correct pluralisation, for example. [See below](#named-property-replacement) for more details.
|
|
790
|
+
|
|
791
|
+
Substitution can be done using either **positional** replacement, or with **named properties**:
|
|
792
|
+
|
|
793
|
+
#### Positional replacement
|
|
787
794
|
|
|
788
795
|
The values in the `substitutions` array are replaced in the original `string` by matching their order to the numerical order of the parameters.
|
|
789
796
|
|
|
@@ -804,7 +811,9 @@ e.g.
|
|
|
804
811
|
|
|
805
812
|
{
|
|
806
813
|
operator: 'replace',
|
|
807
|
-
|
|
814
|
+
// Using $1, $2 instead of %1, %2 this time:
|
|
815
|
+
string: '$1 is actually $2 $3',
|
|
816
|
+
substitutionCharacter: "$",
|
|
808
817
|
substitutions: [
|
|
809
818
|
// Using the 'user' object from above (OBJECT_PROPERTIES operator)
|
|
810
819
|
{
|
|
@@ -830,9 +839,19 @@ e.g.
|
|
|
830
839
|
substitutions: ['bird', 'Tweet!'],
|
|
831
840
|
}
|
|
832
841
|
// => 'A bird says: "Tweet! Tweet! Tweet!"'
|
|
842
|
+
|
|
843
|
+
// Replacement tokens can be escaped using the standard "\" escape character:
|
|
844
|
+
{
|
|
845
|
+
operator: 'stringSubstitution',
|
|
846
|
+
string: 'The price of $1 is \$5',
|
|
847
|
+
subChar: '$',
|
|
848
|
+
substitutions: [ 'a coffee', 'not used' ],
|
|
849
|
+
}
|
|
850
|
+
// => The price of a coffee is $5
|
|
833
851
|
```
|
|
834
852
|
|
|
835
|
-
`children` array: `[string, ...substitutions]`
|
|
853
|
+
`children` array: `[string, ...substitutions]`
|
|
854
|
+
(`trimWhiteSpace` and `substitutionCharacter` not available, since `substitutions` can be an arbitrary number of items)
|
|
836
855
|
|
|
837
856
|
e.g.
|
|
838
857
|
```js
|
|
@@ -843,6 +862,77 @@ e.g.
|
|
|
843
862
|
// => "I am Iron Man"
|
|
844
863
|
```
|
|
845
864
|
|
|
865
|
+
#### Named property replacement
|
|
866
|
+
|
|
867
|
+
Replacement tokens can be indicated in the main string with a named value, using `{{<name>}}` syntax, e.g. `"Your name is {{firstName}} {{lastName}}"`. Then the `substitutions` property must be an object with those property names:
|
|
868
|
+
|
|
869
|
+
```js
|
|
870
|
+
{
|
|
871
|
+
operator: 'stringSubstitution',
|
|
872
|
+
string: 'Your name is {{firstName}} {{lastName}}',
|
|
873
|
+
substitutions: {
|
|
874
|
+
firstName: 'Steve',
|
|
875
|
+
lastName: "Rogers"
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
// => "Your name is Steve Rogers"
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
If the replacement values are numbers, we can extend this functionality with a special `numberMapping` object, which allows for different replacements depending on the value, which is handy for pluralisation, for example.
|
|
882
|
+
|
|
883
|
+
The syntax for the `numberMapping` property is:
|
|
884
|
+
```js
|
|
885
|
+
{
|
|
886
|
+
propertyName1: {
|
|
887
|
+
1: "Output if value is {}",
|
|
888
|
+
2: "Output if value is 2",
|
|
889
|
+
">5": "Output if value is greater than 5",
|
|
890
|
+
"<0": "Output if value is less than 0"
|
|
891
|
+
"other": "Fallback output if none of the others match: {} count"
|
|
892
|
+
// {} is a replacement for the numerical value itself
|
|
893
|
+
},
|
|
894
|
+
propertyName2: { ...etc }
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
The number map can have as few or as many match options as desired -- if no match is found (or if no `numberMapping` property at all), the number will be returned as-is.
|
|
898
|
+
|
|
899
|
+
e.g.
|
|
900
|
+
```js
|
|
901
|
+
{
|
|
902
|
+
operator: 'stringSubstitution',
|
|
903
|
+
string: 'Hi {{name}}, we have {{count}} attending this event.',
|
|
904
|
+
values: {
|
|
905
|
+
name: "Tatiana",
|
|
906
|
+
count: {operator: "getData", property: "numOfPeople" }
|
|
907
|
+
},
|
|
908
|
+
numberMap: {
|
|
909
|
+
count: {
|
|
910
|
+
0: "no one",
|
|
911
|
+
1: "just one person",
|
|
912
|
+
">10": "too many people",
|
|
913
|
+
"other": "{} people"
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
// Output with varying values for "numOfPeople" passed into evaluation
|
|
918
|
+
// "data" object:
|
|
919
|
+
|
|
920
|
+
// { numOfPeople: 5 }
|
|
921
|
+
// => "Hi Tatiana, we have 5 people attending this event."
|
|
922
|
+
|
|
923
|
+
// { numOfPeople: 0 }
|
|
924
|
+
// => "Hi Tatiana, we have no one attending this event."
|
|
925
|
+
|
|
926
|
+
// { numOfPeople: 100 }
|
|
927
|
+
// => "Hi Tatiana, we have too many people attending this event."
|
|
928
|
+
|
|
929
|
+
// { numOfPeople: 1 }
|
|
930
|
+
// => "Hi Tatiana, we have just one person attending this event.
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
**Note**: `children` array not available for named properties
|
|
934
|
+
|
|
935
|
+
|
|
846
936
|
----
|
|
847
937
|
|
|
848
938
|
### SPLIT
|
|
@@ -1703,14 +1793,18 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
|
|
|
1703
1793
|
|
|
1704
1794
|
*Trivial upgrades (e.g. documentation, small re-factors, types, etc.) not included*
|
|
1705
1795
|
|
|
1796
|
+
- **v2.10.0**: Extended stringSubstitution functionality to included named
|
|
1797
|
+
property substitution, trim whitespace option, and pluralisation (#97)
|
|
1706
1798
|
- **v2.9.0**: Added ability to invalidate cache by time (#94)
|
|
1707
1799
|
- **v2.8.6**: Small bug fix where `options` object would be mutated instead of replaced
|
|
1708
1800
|
- **v2.8.5**: Small bug fix in [COUNT](#count) operator
|
|
1709
1801
|
- **v2.8.4**: Refactor types, better compliance with [ESLint](https://eslint.org/) rules, add more tests
|
|
1710
1802
|
- **v2.8.0**:
|
|
1711
1803
|
- **[Shorthand syntax](#shorthand-syntax)** (#80)
|
|
1712
|
-
- **Methods to retrieve [metadata](#metadata)** about operators, fragments and
|
|
1713
|
-
|
|
1804
|
+
- **Methods to retrieve [metadata](#metadata)** about operators, fragments and
|
|
1805
|
+
functions (#82)
|
|
1806
|
+
- **v2.7.0**: **Add `excludeOperators` option** to allow certain operators to be
|
|
1807
|
+
prohibited (e.g. database lookups) (#54)
|
|
1714
1808
|
- **v2.6.0**: Resolve alias nodes that are not part of an Operator node when `evaluateFullObject` is enabled (#78)
|
|
1715
1809
|
- **v2.5.0**:
|
|
1716
1810
|
- Bug fixes for edge cases (mainly related to backwards compatibility)
|
|
@@ -1732,7 +1826,8 @@ Please open an issue: https://github.com/CarlosNZ/fig-tree-evaluator/issues
|
|
|
1732
1826
|
- **v2.0.0**: Re-write as stand-alone package. Major improvements include:
|
|
1733
1827
|
- more [operators](#operator-reference)
|
|
1734
1828
|
- operator (and property) [aliases](#operator--property-aliases)
|
|
1735
|
-
- more appropriately-named properties associated with each operator (as
|
|
1829
|
+
- more appropriately-named properties associated with each operator (as
|
|
1830
|
+
opposed to a single `children` array)
|
|
1736
1831
|
- class-based Evaluator instances
|
|
1737
1832
|
- runtime type-checking
|
|
1738
1833
|
- better error handling and error reporting
|
|
@@ -15,9 +15,30 @@ const parameters = [
|
|
|
15
15
|
{
|
|
16
16
|
name: 'substitutions',
|
|
17
17
|
description: 'An array of substitution values for the parameterised string',
|
|
18
|
-
aliases: ['replacements'],
|
|
18
|
+
aliases: ['replacements', 'values'],
|
|
19
19
|
required: true,
|
|
20
|
-
type: 'array',
|
|
20
|
+
type: ['array', 'object'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'trimWhiteSpace',
|
|
24
|
+
description: 'Whether or not to trim white space from either end of the substituted strings (default: true)',
|
|
25
|
+
aliases: ['trim', 'trimWhitespace'],
|
|
26
|
+
required: false,
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'substitutionCharacter',
|
|
31
|
+
description: 'Which character to search for in original string for replacement -- can be "%" or "$" (default: "%")',
|
|
32
|
+
aliases: ['subCharacter', 'subChar'],
|
|
33
|
+
required: false,
|
|
34
|
+
type: 'string',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'numberMapping',
|
|
38
|
+
description: 'Rules for mapping number values to text strings, such as pluralisation.',
|
|
39
|
+
aliases: ['numMap', 'numberMap', 'pluralisation', 'pluralization', 'plurals'],
|
|
40
|
+
required: false,
|
|
41
|
+
type: 'object',
|
|
21
42
|
},
|
|
22
43
|
];
|
|
23
44
|
exports.propertyAliases = (0, _operatorUtils_1.getPropertyAliases)(parameters);
|
|
@@ -36,15 +36,42 @@ exports.STRING_SUBSTITUTION = void 0;
|
|
|
36
36
|
const _operatorUtils_1 = require("../_operatorUtils");
|
|
37
37
|
const data_1 = __importStar(require("./data"));
|
|
38
38
|
const evaluate = (expression, config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
const [string, substitutions] = (yield (0, _operatorUtils_1.evaluateArray)([
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
const [string, substitutions, trimWhiteSpace = true, substitutionCharacter = '%', numberMapping = {},] = (yield (0, _operatorUtils_1.evaluateArray)([
|
|
40
|
+
expression.string,
|
|
41
|
+
expression.substitutions,
|
|
42
|
+
expression.trimWhiteSpace,
|
|
43
|
+
expression.substitutionCharacter,
|
|
44
|
+
expression.numberMapping,
|
|
45
|
+
], config));
|
|
46
|
+
config.typeChecker((0, _operatorUtils_1.getTypeCheckInput)(data_1.default.parameters, {
|
|
47
|
+
string,
|
|
48
|
+
substitutions,
|
|
49
|
+
trimWhiteSpace,
|
|
50
|
+
substitutionCharacter,
|
|
51
|
+
numberMapping,
|
|
52
|
+
}));
|
|
53
|
+
if (Array.isArray(substitutions)) {
|
|
54
|
+
const subChar = substitutionCharacter === '$' ? '$' : '%';
|
|
55
|
+
const patternString = `(?<!\\\\)(${subChar === '%' ? '%' : '\\$'}[\\d]+)`;
|
|
56
|
+
const parameterPattern = new RegExp(patternString, 'g');
|
|
57
|
+
const parameters = (string.match(parameterPattern) || []).sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)));
|
|
58
|
+
const uniqueParameters = new Set(parameters);
|
|
59
|
+
const replacementsObj = (0, _operatorUtils_1.zipArraysToObject)(Array.from(uniqueParameters), substitutions.map((sub) => (trimWhiteSpace ? String(sub).trim() : sub)));
|
|
60
|
+
return (string
|
|
61
|
+
.split(parameterPattern)
|
|
62
|
+
.map((fragment) => (fragment in replacementsObj ? replacementsObj[fragment] : fragment))
|
|
63
|
+
.join('')
|
|
64
|
+
.replace(`\\${subChar}`, subChar));
|
|
65
|
+
}
|
|
66
|
+
const parameterPattern = /(?<!\\)({{[A-z0-9_]+}})/g;
|
|
45
67
|
return string
|
|
46
68
|
.split(parameterPattern)
|
|
47
|
-
.map((fragment) =>
|
|
69
|
+
.map((fragment) => {
|
|
70
|
+
if (!/(?<!\\){{(.+)}}/.exec(fragment))
|
|
71
|
+
return fragment.replace('\\{{', '{{');
|
|
72
|
+
const replacement = getReplacement(fragment, substitutions, numberMapping);
|
|
73
|
+
return trimWhiteSpace ? String(replacement).trim() : replacement;
|
|
74
|
+
})
|
|
48
75
|
.join('');
|
|
49
76
|
});
|
|
50
77
|
const parseChildren = (expression) => {
|
|
@@ -57,3 +84,29 @@ exports.STRING_SUBSTITUTION = {
|
|
|
57
84
|
evaluate,
|
|
58
85
|
parseChildren,
|
|
59
86
|
};
|
|
87
|
+
const getReplacement = (fragment, replacements, numberMaps) => {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
const key = fragment.replace(/{{(.+)}}/, '$1');
|
|
90
|
+
const value = (_a = replacements === null || replacements === void 0 ? void 0 : replacements[key]) !== null && _a !== void 0 ? _a : '';
|
|
91
|
+
if (typeof value !== 'number')
|
|
92
|
+
return (_b = replacements === null || replacements === void 0 ? void 0 : replacements[key]) !== null && _b !== void 0 ? _b : '';
|
|
93
|
+
if (!(key in numberMaps))
|
|
94
|
+
return value;
|
|
95
|
+
const numMap = numberMaps[key];
|
|
96
|
+
if (value in numMap)
|
|
97
|
+
return numMap[value].replace('{}', String(value));
|
|
98
|
+
const numberKeys = Object.keys(numberMaps[key]);
|
|
99
|
+
const greaterThanKey = numberKeys.find((key) => key.startsWith('>'));
|
|
100
|
+
if (greaterThanKey) {
|
|
101
|
+
const num = Number(greaterThanKey.slice(1));
|
|
102
|
+
if (value > num)
|
|
103
|
+
return numMap === null || numMap === void 0 ? void 0 : numMap[greaterThanKey];
|
|
104
|
+
}
|
|
105
|
+
const lessThanKey = numberKeys.find((key) => key.startsWith('<'));
|
|
106
|
+
if (lessThanKey) {
|
|
107
|
+
const num = Number(lessThanKey.slice(1));
|
|
108
|
+
if (value < num)
|
|
109
|
+
return numMap === null || numMap === void 0 ? void 0 : numMap[lessThanKey];
|
|
110
|
+
}
|
|
111
|
+
return numMap.other ? numMap.other.replace('{}', String(value)) : value;
|
|
112
|
+
};
|
package/build/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.
|
|
1
|
+
export declare const version = "2.10.0";
|
package/build/version.js
CHANGED