@nocobase/evaluators 0.9.1-alpha.1
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/client.d.ts +3 -0
- package/client.js +12 -0
- package/lib/client/engines/formulajs.d.ts +7 -0
- package/lib/client/engines/formulajs.js +20 -0
- package/lib/client/engines/mathjs.d.ts +7 -0
- package/lib/client/engines/mathjs.js +20 -0
- package/lib/client/index.d.ts +12 -0
- package/lib/client/index.js +83 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +30 -0
- package/lib/server/index.d.ts +5 -0
- package/lib/server/index.js +31 -0
- package/lib/utils/formulajs.d.ts +1 -0
- package/lib/utils/formulajs.js +38 -0
- package/lib/utils/index.d.ts +5 -0
- package/lib/utils/index.js +73 -0
- package/lib/utils/mathjs.d.ts +1 -0
- package/lib/utils/mathjs.js +34 -0
- package/package.json +19 -0
- package/server.d.ts +3 -0
- package/server.js +12 -0
- package/src/client/engines/formulajs.ts +11 -0
- package/src/client/engines/mathjs.ts +11 -0
- package/src/client/index.tsx +47 -0
- package/src/index.ts +2 -0
- package/src/server/__tests__/index.test.ts +42 -0
- package/src/server/index.ts +16 -0
- package/src/utils/formulajs.ts +18 -0
- package/src/utils/index.ts +61 -0
- package/src/utils/mathjs.ts +12 -0
package/client.d.ts
ADDED
package/client.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var _useExportClient = require("./lib/client");
|
|
2
|
+
|
|
3
|
+
Object.keys(_useExportClient).forEach(function (key) {
|
|
4
|
+
if (key === "default" || key === "__esModule") return;
|
|
5
|
+
if (key in exports && exports[key] === _useExportClient[key]) return;
|
|
6
|
+
Object.defineProperty(exports, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _useExportClient[key];
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("../../utils");
|
|
9
|
+
|
|
10
|
+
var _formulajs = _interopRequireDefault(require("../../utils/formulajs"));
|
|
11
|
+
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
|
|
14
|
+
var _default = {
|
|
15
|
+
label: 'Formula.js',
|
|
16
|
+
tooltip: '{{t("Formula.js supports most Microsoft Excel formula functions.")}}',
|
|
17
|
+
link: 'https://formulajs.info/functions/',
|
|
18
|
+
evaluate: _utils.evaluate.bind(_formulajs.default)
|
|
19
|
+
};
|
|
20
|
+
exports.default = _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("../../utils");
|
|
9
|
+
|
|
10
|
+
var _mathjs = _interopRequireDefault(require("../../utils/mathjs"));
|
|
11
|
+
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
|
|
14
|
+
var _default = {
|
|
15
|
+
label: 'Math.js',
|
|
16
|
+
tooltip: `{{t('Math.js comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types')}}`,
|
|
17
|
+
link: "https://mathjs.org/",
|
|
18
|
+
evaluate: _utils.evaluate.bind(_mathjs.default)
|
|
19
|
+
};
|
|
20
|
+
exports.default = _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Registry } from '@nocobase/utils/client';
|
|
2
|
+
export interface Evaluator {
|
|
3
|
+
label: string;
|
|
4
|
+
tooltip?: string;
|
|
5
|
+
link?: string;
|
|
6
|
+
evaluate(exp: string, scope?: {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}): any;
|
|
9
|
+
}
|
|
10
|
+
export declare const evaluators: Registry<Evaluator>;
|
|
11
|
+
export declare const renderReference: (key: string) => JSX.Element;
|
|
12
|
+
export default evaluators;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.renderReference = exports.evaluators = exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
function _react() {
|
|
9
|
+
const data = _interopRequireDefault(require("react"));
|
|
10
|
+
|
|
11
|
+
_react = function _react() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _css() {
|
|
19
|
+
const data = require("@emotion/css");
|
|
20
|
+
|
|
21
|
+
_css = function _css() {
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _client() {
|
|
29
|
+
const data = require("@nocobase/client");
|
|
30
|
+
|
|
31
|
+
_client = function _client() {
|
|
32
|
+
return data;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function _client2() {
|
|
39
|
+
const data = require("@nocobase/utils/client");
|
|
40
|
+
|
|
41
|
+
_client2 = function _client2() {
|
|
42
|
+
return data;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var _mathjs = _interopRequireDefault(require("./engines/mathjs"));
|
|
49
|
+
|
|
50
|
+
var _formulajs = _interopRequireDefault(require("./engines/formulajs"));
|
|
51
|
+
|
|
52
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
53
|
+
|
|
54
|
+
const evaluators = new (_client2().Registry)();
|
|
55
|
+
exports.evaluators = evaluators;
|
|
56
|
+
evaluators.register('math.js', _mathjs.default);
|
|
57
|
+
evaluators.register('formula.js', _formulajs.default);
|
|
58
|
+
|
|
59
|
+
const renderReference = key => {
|
|
60
|
+
const engine = evaluators.get(key);
|
|
61
|
+
|
|
62
|
+
if (!engine) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return engine.link ? _react().default.createElement(_react().default.Fragment, null, _react().default.createElement("span", {
|
|
67
|
+
className: (0, _css().css)`
|
|
68
|
+
&:after {
|
|
69
|
+
content: ':';
|
|
70
|
+
}
|
|
71
|
+
& + a {
|
|
72
|
+
margin-left: .25em;
|
|
73
|
+
}
|
|
74
|
+
`
|
|
75
|
+
}, _client().i18n.t('Syntax references')), _react().default.createElement("a", {
|
|
76
|
+
href: engine.link,
|
|
77
|
+
target: "_blank"
|
|
78
|
+
}, engine.label)) : null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
exports.renderReference = renderReference;
|
|
82
|
+
var _default = evaluators;
|
|
83
|
+
exports.default = _default;
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {};
|
|
7
|
+
Object.defineProperty(exports, "default", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function get() {
|
|
10
|
+
return _server.default;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
var _server = _interopRequireWildcard(require("./server"));
|
|
15
|
+
|
|
16
|
+
Object.keys(_server).forEach(function (key) {
|
|
17
|
+
if (key === "default" || key === "__esModule") return;
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
19
|
+
if (key in exports && exports[key] === _server[key]) return;
|
|
20
|
+
Object.defineProperty(exports, key, {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function get() {
|
|
23
|
+
return _server[key];
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
29
|
+
|
|
30
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.evaluators = exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
function _utils() {
|
|
9
|
+
const data = require("@nocobase/utils");
|
|
10
|
+
|
|
11
|
+
_utils = function _utils() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
var _utils2 = require("../utils");
|
|
19
|
+
|
|
20
|
+
var _mathjs = _interopRequireDefault(require("../utils/mathjs"));
|
|
21
|
+
|
|
22
|
+
var _formulajs = _interopRequireDefault(require("../utils/formulajs"));
|
|
23
|
+
|
|
24
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
|
+
|
|
26
|
+
const evaluators = new (_utils().Registry)();
|
|
27
|
+
exports.evaluators = evaluators;
|
|
28
|
+
evaluators.register('math.js', _utils2.evaluate.bind(_mathjs.default));
|
|
29
|
+
evaluators.register('formula.js', _utils2.evaluate.bind(_formulajs.default));
|
|
30
|
+
var _default = evaluators;
|
|
31
|
+
exports.default = _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (expression: string, scope?: {}): any;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = _default;
|
|
7
|
+
|
|
8
|
+
function functions() {
|
|
9
|
+
const data = _interopRequireWildcard(require("@formulajs/formulajs"));
|
|
10
|
+
|
|
11
|
+
functions = function functions() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
19
|
+
|
|
20
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
21
|
+
|
|
22
|
+
const fnNames = Object.keys(functions()).filter(key => key !== 'default');
|
|
23
|
+
const fns = fnNames.map(key => functions()[key]);
|
|
24
|
+
|
|
25
|
+
function _default(expression, scope = {}) {
|
|
26
|
+
const fn = new Function(...fnNames, ...Object.keys(scope), `return ${expression}`);
|
|
27
|
+
const result = fn(...fns, ...Object.values(scope));
|
|
28
|
+
|
|
29
|
+
if (typeof result === 'number') {
|
|
30
|
+
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return functions().ROUND(result, 9);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.evaluate = evaluate;
|
|
7
|
+
|
|
8
|
+
function _lodash() {
|
|
9
|
+
const data = require("lodash");
|
|
10
|
+
|
|
11
|
+
_lodash = function _lodash() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function appendArrayColumn(scope, key) {
|
|
19
|
+
const paths = key.split('.');
|
|
20
|
+
let data = scope;
|
|
21
|
+
|
|
22
|
+
for (let p = 0; p < paths.length && data != null; p++) {
|
|
23
|
+
const path = paths[p];
|
|
24
|
+
const isIndex = path.match(/^\d+$/);
|
|
25
|
+
|
|
26
|
+
if (Array.isArray(data) && !isIndex && !data[path]) {
|
|
27
|
+
data[path] = data.map(item => item[path]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
data = data[path];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function replaceNumberIndex(path, scope) {
|
|
35
|
+
const segments = path.split('.');
|
|
36
|
+
const paths = [];
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < segments.length; i++) {
|
|
39
|
+
const p = segments[i];
|
|
40
|
+
|
|
41
|
+
if (p.match(/^\d+$/)) {
|
|
42
|
+
paths.push(Array.isArray((0, _lodash().get)(scope, segments.slice(0, i))) ? `[${p}]` : `["${p}"]`);
|
|
43
|
+
} else {
|
|
44
|
+
if (i) {
|
|
45
|
+
paths.push('.', p);
|
|
46
|
+
} else {
|
|
47
|
+
paths.push(p);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return paths.join('');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function evaluate(expression, scope = {}) {
|
|
56
|
+
const context = (0, _lodash().cloneDeep)(scope);
|
|
57
|
+
const exp = expression.trim().replace(/{{\s*([^{}]+)\s*}}/g, (_, v) => {
|
|
58
|
+
appendArrayColumn(context, v);
|
|
59
|
+
const item = (0, _lodash().get)(context, v);
|
|
60
|
+
|
|
61
|
+
if (item == null) {
|
|
62
|
+
return 'null';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof item === 'function') {
|
|
66
|
+
const result = item();
|
|
67
|
+
return typeof result === 'string' ? `'${result.replace(/'/g, "\\'")}'` : result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return replaceNumberIndex(v, context);
|
|
71
|
+
});
|
|
72
|
+
return this(exp, context);
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function (expression: string, scope?: {}): any;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = _default;
|
|
7
|
+
|
|
8
|
+
function math() {
|
|
9
|
+
const data = _interopRequireWildcard(require("mathjs"));
|
|
10
|
+
|
|
11
|
+
math = function math() {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
19
|
+
|
|
20
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
21
|
+
|
|
22
|
+
function _default(expression, scope = {}) {
|
|
23
|
+
const result = math().evaluate(expression, scope);
|
|
24
|
+
|
|
25
|
+
if (typeof result === 'number') {
|
|
26
|
+
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return math().round(result, 9);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nocobase/evaluators",
|
|
3
|
+
"version": "0.9.1-alpha.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./lib/index.d.ts",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@formulajs/formulajs": "4.2.0",
|
|
10
|
+
"@nocobase/utils": "0.9.1-alpha.1",
|
|
11
|
+
"mathjs": "^10.6.0"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
16
|
+
"directory": "packages/evaluators"
|
|
17
|
+
},
|
|
18
|
+
"gitHead": "56cb184b00dc383b853015d525bf6e79dea92169"
|
|
19
|
+
}
|
package/server.d.ts
ADDED
package/server.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var _useExportServer = require("./lib/server");
|
|
2
|
+
|
|
3
|
+
Object.keys(_useExportServer).forEach(function (key) {
|
|
4
|
+
if (key === "default" || key === "__esModule") return;
|
|
5
|
+
if (key in exports && exports[key] === _useExportServer[key]) return;
|
|
6
|
+
Object.defineProperty(exports, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _useExportServer[key];
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { evaluate } from '../../utils';
|
|
2
|
+
import formulajs from '../../utils/formulajs';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
label: 'Formula.js',
|
|
8
|
+
tooltip: '{{t("Formula.js supports most Microsoft Excel formula functions.")}}',
|
|
9
|
+
link: 'https://formulajs.info/functions/',
|
|
10
|
+
evaluate: evaluate.bind(formulajs)
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { evaluate } from "../../utils";
|
|
2
|
+
import mathjs from "../../utils/mathjs";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
label: 'Math.js',
|
|
8
|
+
tooltip: `{{t('Math.js comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types')}}`,
|
|
9
|
+
link: "https://mathjs.org/",
|
|
10
|
+
evaluate: evaluate.bind(mathjs)
|
|
11
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { css } from '@emotion/css';
|
|
3
|
+
|
|
4
|
+
import { i18n } from '@nocobase/client';
|
|
5
|
+
import { Registry } from '@nocobase/utils/client';
|
|
6
|
+
|
|
7
|
+
import mathjs from './engines/mathjs';
|
|
8
|
+
import formulajs from './engines/formulajs';
|
|
9
|
+
|
|
10
|
+
export interface Evaluator {
|
|
11
|
+
label: string;
|
|
12
|
+
tooltip?: string;
|
|
13
|
+
link?: string;
|
|
14
|
+
evaluate(exp: string, scope?: { [key: string]: any }): any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const evaluators = new Registry<Evaluator>();
|
|
18
|
+
|
|
19
|
+
evaluators.register('math.js', mathjs);
|
|
20
|
+
evaluators.register('formula.js', formulajs);
|
|
21
|
+
|
|
22
|
+
export const renderReference = (key: string) => {
|
|
23
|
+
const engine = evaluators.get(key);
|
|
24
|
+
if (!engine) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return engine.link
|
|
29
|
+
? (
|
|
30
|
+
<>
|
|
31
|
+
<span className={css`
|
|
32
|
+
&:after {
|
|
33
|
+
content: ':';
|
|
34
|
+
}
|
|
35
|
+
& + a {
|
|
36
|
+
margin-left: .25em;
|
|
37
|
+
}
|
|
38
|
+
`}>
|
|
39
|
+
{i18n.t('Syntax references')}
|
|
40
|
+
</span>
|
|
41
|
+
<a href={engine.link} target="_blank">{engine.label}</a>
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
: null
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default evaluators;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import evaluators from '..';
|
|
2
|
+
|
|
3
|
+
const mathEval = evaluators.get('math.js');
|
|
4
|
+
const formulaEval = evaluators.get('formula.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
describe('evaluate', () => {
|
|
9
|
+
it('reference null or undefined', () => {
|
|
10
|
+
const result = formulaEval('{{a.b}}', { a: null });
|
|
11
|
+
expect(result).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('function result string with quote', () => {
|
|
15
|
+
const result = formulaEval('{{a}}', { a() { return "I'm done." } });
|
|
16
|
+
expect(result).toBe("I'm done.");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('function result number', () => {
|
|
20
|
+
const result = formulaEval('{{a}}', { a() { return 1 } });
|
|
21
|
+
expect(result).toBe(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('number path to array item 0 (math.js)', () => {
|
|
25
|
+
expect(() => mathEval('{{a.0}}', { a: [1, 2, 3] })).toThrow();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('number path to array item 1 (math.js)', () => {
|
|
29
|
+
const result = mathEval('{{a}}[1]', { a: [1, 2, 3] });
|
|
30
|
+
expect(result).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('number path to array item 1 (math.js)', () => {
|
|
34
|
+
const result = mathEval('{{a.1}}', { a: [1, 2, 3] });
|
|
35
|
+
expect(result).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('number path to object member 0 (math.js)', () => {
|
|
39
|
+
const result = mathEval('{{a.1}}', { a: { 1: 1 } });
|
|
40
|
+
expect(result).toBe(1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Registry } from "@nocobase/utils";
|
|
2
|
+
|
|
3
|
+
import { evaluate, Evaluator } from '../utils';
|
|
4
|
+
import mathjs from "../utils/mathjs";
|
|
5
|
+
import formulajs from "../utils/formulajs";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export { Evaluator } from '../utils';
|
|
10
|
+
|
|
11
|
+
export const evaluators = new Registry<Evaluator>();
|
|
12
|
+
|
|
13
|
+
evaluators.register('math.js', evaluate.bind(mathjs));
|
|
14
|
+
evaluators.register('formula.js', evaluate.bind(formulajs));
|
|
15
|
+
|
|
16
|
+
export default evaluators;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as functions from '@formulajs/formulajs';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const fnNames = Object.keys(functions).filter(key => key !== 'default');
|
|
6
|
+
const fns = fnNames.map(key => functions[key]);
|
|
7
|
+
|
|
8
|
+
export default function(expression: string, scope = {}) {
|
|
9
|
+
const fn = new Function(...fnNames, ...Object.keys(scope), `return ${expression}`);
|
|
10
|
+
const result = fn(...fns, ...Object.values(scope));
|
|
11
|
+
if (typeof result === 'number') {
|
|
12
|
+
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return functions.ROUND(result, 9);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { get, cloneDeep } from "lodash";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export type Scope = { [key: string]: any };
|
|
6
|
+
|
|
7
|
+
export type Evaluator = (expression: string, scope?: Scope) => any;
|
|
8
|
+
|
|
9
|
+
function appendArrayColumn(scope, key) {
|
|
10
|
+
const paths = key.split('.');
|
|
11
|
+
let data = scope;
|
|
12
|
+
for (let p = 0; p < paths.length && data != null; p++) {
|
|
13
|
+
const path = paths[p];
|
|
14
|
+
const isIndex = path.match(/^\d+$/);
|
|
15
|
+
if (Array.isArray(data) && !isIndex && !data[path]) {
|
|
16
|
+
data[path] = data.map(item => item[path]);
|
|
17
|
+
}
|
|
18
|
+
data = data[path];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function replaceNumberIndex(path: string, scope: Scope): string {
|
|
23
|
+
const segments = path.split('.');
|
|
24
|
+
const paths = [];
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < segments.length; i++) {
|
|
27
|
+
const p = segments[i];
|
|
28
|
+
if (p.match(/^\d+$/)) {
|
|
29
|
+
paths.push(Array.isArray(get(scope, segments.slice(0, i))) ? `[${p}]` : `["${p}"]`);
|
|
30
|
+
} else {
|
|
31
|
+
if (i) {
|
|
32
|
+
paths.push('.', p);
|
|
33
|
+
} else {
|
|
34
|
+
paths.push(p);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return paths.join('');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function evaluate(this: Evaluator, expression: string, scope: Scope = {}) {
|
|
43
|
+
const context = cloneDeep(scope);
|
|
44
|
+
const exp = expression.trim().replace(/{{\s*([^{}]+)\s*}}/g, (_, v) => {
|
|
45
|
+
appendArrayColumn(context, v);
|
|
46
|
+
|
|
47
|
+
const item = get(context, v);
|
|
48
|
+
|
|
49
|
+
if (item == null) {
|
|
50
|
+
return 'null';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof item === 'function') {
|
|
54
|
+
const result = item();
|
|
55
|
+
return typeof result === 'string' ? `'${result.replace(/'/g, "\\'")}'` : result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return replaceNumberIndex(v, context);
|
|
59
|
+
});
|
|
60
|
+
return this(exp, context);
|
|
61
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as math from 'mathjs';
|
|
2
|
+
|
|
3
|
+
export default function (expression: string, scope = {}) {
|
|
4
|
+
const result = math.evaluate(expression, scope);
|
|
5
|
+
if (typeof result === 'number') {
|
|
6
|
+
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
return math.round(result, 9);
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|