@steedos/objectql 2.1.56 → 2.1.57

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@steedos/objectql",
3
3
  "private": false,
4
- "version": "2.1.56",
4
+ "version": "2.1.57",
5
5
  "description": "",
6
6
  "main": "lib/index.js",
7
7
  "scripts": {
@@ -18,12 +18,12 @@
18
18
  "@salesforce/dev-config": "^1.4.4",
19
19
  "@salesforce/kit": "^1.0.4",
20
20
  "@salesforce/ts-types": "^1.1.1",
21
- "@steedos/filters": "2.1.56",
22
- "@steedos/formula": "2.1.56",
23
- "@steedos/metadata-core": "2.1.56",
21
+ "@steedos/filters": "2.1.57",
22
+ "@steedos/formula": "2.1.57",
23
+ "@steedos/metadata-core": "2.1.57",
24
24
  "@steedos/odata-v4-typeorm": "^1.20.1",
25
- "@steedos/schemas": "2.1.56",
26
- "@steedos/standard-objects": "2.1.56",
25
+ "@steedos/schemas": "2.1.57",
26
+ "@steedos/standard-objects": "2.1.57",
27
27
  "@types/express": "^4.16.1",
28
28
  "@types/mongodb": "^3.1.22",
29
29
  "@types/node": "^11.10.4",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@salesforce/dev-scripts": "0.3.12",
60
- "@steedos/meteor-bundle-runner": "2.1.56",
60
+ "@steedos/meteor-bundle-runner": "2.1.57",
61
61
  "@types/chai": "^4.1.7",
62
62
  "@types/chai-as-promised": "7.1.0",
63
63
  "@types/mocha": "^5.2.6",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "a8eed2e9927d67c38df80be1474cb6b7f5b0159c"
80
+ "gitHead": "ded65c3ad95afec8827dd81d505684459a3ba019"
81
81
  }
@@ -1,6 +1,6 @@
1
1
  import { Dictionary, JsonMap } from "@salesforce/ts-types";
2
2
  import { SteedosTriggerType, SteedosFieldType, SteedosFieldTypeConfig, SteedosSchema, SteedosListenerConfig, SteedosObjectListViewTypeConfig, SteedosObjectListViewType, SteedosIDType, SteedosObjectPermissionTypeConfig, SteedosActionType, SteedosActionTypeConfig, SteedosUserSession, getSteedosSchema } from ".";
3
- import { getUserObjectSharesFilters, isTemplateSpace, isCloudAdminSpace, generateActionParams, absoluteUrl } from '../util'
3
+ import { getUserObjectSharesFilters, isTemplateSpace, isCloudAdminSpace, generateActionParams, absoluteUrl, isExpression, parseSingleExpression } from '../util'
4
4
  import _ = require("underscore");
5
5
  import { SteedosTriggerTypeConfig, SteedosTriggerContextConfig } from "./trigger";
6
6
  import { SteedosQueryOptions, SteedosQueryFilters } from "./query";
@@ -15,7 +15,7 @@ import { brokeEmitEvents } from "./object_events";
15
15
  import { translationObject } from "@steedos/i18n";
16
16
  import { getObjectLayouts } from "./object_layouts";
17
17
  import { sortBy, forEach } from 'lodash';
18
- declare var Creator: any;
18
+
19
19
  const clone = require('clone')
20
20
 
21
21
  // 主子表有层级限制,超过3层就报错,该函数判断当前对象作为主表对象往下的层级最多不越过3层,
@@ -634,7 +634,10 @@ export class SteedosObjectType extends SteedosObjectProperties {
634
634
  disabled_actions: null,
635
635
  unreadable_fields: null,
636
636
  uneditable_fields: null,
637
- unrelated_objects: null
637
+ unrelated_objects: null,
638
+ field_permissions: null,
639
+ read_filters: [],
640
+ edit_filters: []
638
641
  }
639
642
 
640
643
  if (_.isEmpty(roles)) {
@@ -654,20 +657,56 @@ export class SteedosObjectType extends SteedosObjectProperties {
654
657
  if(_.isBoolean(_v)){
655
658
  userObjectPermission[k] = _v
656
659
  }
657
- } else if ((_.isArray(v) || _.isNull(v))) {
660
+ } else if (['read_filters', 'edit_filters'].indexOf(k) > -1) {
661
+ if ('edit_filters' === k) {
662
+ if (!_.isEmpty(_v)) {
663
+ userObjectPermission['read_filters'].push(_v);
664
+ }
665
+ }
666
+ if (!_.isEmpty(_v)) {
667
+ userObjectPermission[k].push(_v);
668
+ }
669
+ }
670
+ else if ((_.isArray(v) || _.isNull(v))) {
658
671
  if (!_.isArray(_v)) {
659
672
  _v = []
660
673
  }
661
674
  if (_.isNull(v)) {
662
675
  userObjectPermission[k] = _v
663
676
  } else {
664
- userObjectPermission[k] = _.intersection(v, _v)
677
+ if (k === 'field_permissions') {
678
+ userObjectPermission[k] = _.union(v, _v)
679
+ } else {
680
+ userObjectPermission[k] = _.intersection(v, _v)
681
+ }
665
682
  }
666
683
  }
667
684
  })
668
685
  }
669
- })
686
+ });
670
687
 
688
+ const field_permissions = {};
689
+ if (userObjectPermission.field_permissions) {
690
+ _.each(userObjectPermission.field_permissions, (field_permission) => {
691
+ const { field, read, edit } = field_permission;
692
+ if (field_permissions[field]) {
693
+ field_permissions[field].edit = field_permissions[field].edit || edit;
694
+ if (field_permissions[field].edit) {
695
+ field_permissions[field].read = true
696
+ } else {
697
+ field_permissions[field].read = field_permissions[field].read || read;
698
+ }
699
+ } else {
700
+ field_permissions[field] = {
701
+ field: field,
702
+ read: edit || read,
703
+ edit: edit
704
+ }
705
+ }
706
+ })
707
+ }
708
+
709
+ userObjectPermission.field_permissions = field_permissions;
671
710
 
672
711
  userObjectPermission.disabled_list_views = userObjectPermission.disabled_list_views || []
673
712
  userObjectPermission.disabled_actions = userObjectPermission.disabled_actions || []
@@ -680,7 +719,6 @@ export class SteedosObjectType extends SteedosObjectProperties {
680
719
  return Object.assign({}, userObjectPermission, { allowRead: true, viewAllRecords: true, viewCompanyRecords: true })
681
720
  }
682
721
 
683
- Creator.processPermissions(userObjectPermission)
684
722
  return userObjectPermission;
685
723
  }
686
724
 
@@ -1021,6 +1059,27 @@ export class SteedosObjectType extends SteedosObjectProperties {
1021
1059
  })
1022
1060
  }
1023
1061
 
1062
+ // 使用字段级安全性作为限制用户对字段的访问权限的手段;然后使用页面布局主要在选项卡中组织详细信息和编辑页面。这可以减少需要维护的页面布局数量。
1063
+ // 例如,如果字段在页面布局中是必需的,并且在字段级安全性设置中是只读的,则字段级安全性将覆盖页面布局,并且该字段将对用户是只读的。
1064
+ const userObjectFields = objectConfig.fields;
1065
+ _.each(objectConfig.permissions.field_permissions, (field_permission, field) => {
1066
+ const { read, edit } = field_permission;
1067
+ if (read) {
1068
+ userObjectFields[field].omit = true;
1069
+ }
1070
+ if (edit) {
1071
+ userObjectFields[field].omit = false;
1072
+ userObjectFields[field].hidden = false;
1073
+ userObjectFields[field].readonly = false;
1074
+ userObjectFields[field].disabled = false;
1075
+ }
1076
+ if (!read && !edit) {
1077
+ delete userObjectFields[field]
1078
+ }
1079
+ })
1080
+
1081
+ objectConfig.fields = userObjectFields
1082
+
1024
1083
  // TODO object layout 是否需要控制审批记录显示?
1025
1084
  let spaceProcessDefinition = await getObject("process_definition").directFind({ filters: [['space', '=', userSession.spaceId], ['object_name', '=', this.name], ['active', '=', true]] })
1026
1085
  if (spaceProcessDefinition.length > 0) {
@@ -1256,6 +1315,32 @@ export class SteedosObjectType extends SteedosObjectProperties {
1256
1315
  return await this.runTriggerActions(when, context)
1257
1316
  }
1258
1317
 
1318
+ private async appendRecordPermission(records, userSession) {
1319
+ const _ids = _.pluck(records, '_id');
1320
+ const objPm = await this.getUserObjectPermission(userSession);
1321
+ const permissionFilters = this.getObjectPermissionFilters(objPm, userSession, true);
1322
+ if (_.isEmpty(permissionFilters)) {
1323
+ return;
1324
+ }
1325
+ const filters = formatFiltersToODataQuery(['_id', 'in', _ids])
1326
+
1327
+ const results = await this.directFind({
1328
+ fields: ['_id'],
1329
+ filters: `(${filters}) and (${permissionFilters.join(' or ')})`
1330
+ });
1331
+ const allowEditIds = _.pluck(results, '_id');
1332
+ _.each(records, (record) => {
1333
+ if (_.include(allowEditIds, record._id)) {
1334
+ record.record_permissions = {
1335
+ allowRead: true,
1336
+ allowEdit: true,
1337
+ allowDelete: true,
1338
+ }
1339
+ }
1340
+ })
1341
+
1342
+ }
1343
+
1259
1344
  private async getTriggerContext(when: string, method: string, args: any[], recordId?: string) {
1260
1345
 
1261
1346
  let userSession = args[args.length - 1]
@@ -1406,6 +1491,14 @@ export class SteedosObjectType extends SteedosObjectProperties {
1406
1491
  let values = returnValue || {}
1407
1492
  if (method === 'count') {
1408
1493
  values = returnValue || 0
1494
+ } else {
1495
+ if (userSession) {
1496
+ let _records = returnValue
1497
+ if (method == 'findOne' && returnValue) {
1498
+ _records = [_records]
1499
+ }
1500
+ await this.appendRecordPermission(_records, userSession);
1501
+ }
1409
1502
  }
1410
1503
  Object.assign(afterTriggerContext, { data: { values: values } })
1411
1504
  }
@@ -1484,6 +1577,33 @@ export class SteedosObjectType extends SteedosObjectProperties {
1484
1577
  }
1485
1578
  }
1486
1579
 
1580
+ private getObjectPermissionFilters(objectPermission, userSession, isEdit) {
1581
+ const objectPermissionFilters = [];
1582
+
1583
+ let permissionFiltersKey = 'read_filters';
1584
+ if (isEdit) {
1585
+ permissionFiltersKey = 'edit_filters';
1586
+ }
1587
+
1588
+ const globalData = Object.assign({}, userSession, { now: new Date() });
1589
+
1590
+ let permissionFilters = objectPermission[permissionFiltersKey];
1591
+
1592
+ _.each(permissionFilters, (permissionFilter) => {
1593
+ if (_.isString(permissionFilter) && isExpression(permissionFilter.trim())) {
1594
+ try {
1595
+ const filters = parseSingleExpression(permissionFilter, {}, "#", globalData);
1596
+ if (filters && !_.isString(filters)) {
1597
+ objectPermissionFilters.push(`(${formatFiltersToODataQuery(filters, userSession)})`)
1598
+ }
1599
+ } catch (error) {
1600
+ console.error(`getObjectPermissionFilters error`, permissionFilter, error.message);
1601
+ }
1602
+ }
1603
+ })
1604
+ return objectPermissionFilters;
1605
+ }
1606
+
1487
1607
  /**
1488
1608
  * 把query.filters用formatFiltersToODataQuery转为odata query
1489
1609
  * 主要是为了把userSession中的utcOffset逻辑传入formatFiltersToODataQuery函数处理
@@ -1514,7 +1634,6 @@ export class SteedosObjectType extends SteedosObjectProperties {
1514
1634
  if (method === 'aggregate' || method === 'aggregatePrefixalPipeline') {
1515
1635
  query = args[args.length - 3];
1516
1636
  }
1517
-
1518
1637
  if (query.filters && !_.isString(query.filters)) {
1519
1638
  query.filters = formatFiltersToODataQuery(query.filters);
1520
1639
  }
@@ -1527,7 +1646,7 @@ export class SteedosObjectType extends SteedosObjectProperties {
1527
1646
  return
1528
1647
  }
1529
1648
 
1530
- let spaceFilter, companyFilter, ownerFilter, sharesFilter, clientFilter = query.filters, filters, permissionFilters = [], userFilters = [];
1649
+ let spaceFilter, companyFilter, ownerFilter, sharesFilter, objectPermissionFilters, clientFilter = query.filters, filters, permissionFilters = [], userFilters = [];
1531
1650
 
1532
1651
  if (spaceId) {
1533
1652
  spaceFilter = `(space eq '${spaceId}')`;
@@ -1547,10 +1666,14 @@ export class SteedosObjectType extends SteedosObjectProperties {
1547
1666
  ownerFilter = `(owner eq '${userId}')`;
1548
1667
  }
1549
1668
 
1550
- if (!objPm.viewAllRecords) {
1551
- sharesFilter = getUserObjectSharesFilters(this.name, userSession);
1669
+ objectPermissionFilters = this.getObjectPermissionFilters(objPm, userSession, false);
1670
+
1671
+ if (!_.isEmpty(objectPermissionFilters)) {
1672
+ permissionFilters.push(`(${objectPermissionFilters.join(' or ')})`);
1552
1673
  }
1553
1674
 
1675
+ sharesFilter = getUserObjectSharesFilters(this.name, userSession);
1676
+
1554
1677
  if (!_.isEmpty(companyFilter)) {
1555
1678
  permissionFilters.push(`(${companyFilter.join(' or ')})`);
1556
1679
  }
@@ -1587,9 +1710,14 @@ export class SteedosObjectType extends SteedosObjectProperties {
1587
1710
  }
1588
1711
  }
1589
1712
  else if (method === 'update' || method === 'updateOne') {
1590
- if (!objPm.allowEdit) {
1713
+ const permissionFilters = this.getObjectPermissionFilters(objPm, userSession, true);
1714
+ if (!objPm.allowEdit && _.isEmpty(permissionFilters)) {
1591
1715
  throw new Error(`no ${method} permission!`);
1592
1716
  }
1717
+ let objectPermissionEditFilters = '';
1718
+ if (!_.isEmpty(permissionFilters)) {
1719
+ objectPermissionEditFilters = ` or (${permissionFilters.join(' or ')})`
1720
+ }
1593
1721
  let id = args[args.length - 3];
1594
1722
  if (!objPm.modifyAllRecords && objPm.modifyCompanyRecords) {
1595
1723
  let companyFilters = _.map(userSession.companies, function (comp: any) {
@@ -1597,25 +1725,35 @@ export class SteedosObjectType extends SteedosObjectProperties {
1597
1725
  }).join(' or ')
1598
1726
  if (companyFilters) {
1599
1727
  if (_.isString(id)) {
1600
- id = { filters: `(_id eq \'${id}\') and (${companyFilters})` }
1728
+ id = { filters: `(_id eq \'${id}\') and (${companyFilters}${objectPermissionEditFilters})` }
1601
1729
  }
1602
1730
  else if (_.isObject(id)) {
1603
1731
  if (id.filters && !_.isString(id.filters)) {
1604
1732
  id.filters = formatFiltersToODataQuery(id.filters);
1605
1733
  }
1606
- id.filters = id.filters ? `(${id.filters}) and (${companyFilters})` : `(${companyFilters})`;
1734
+ id.filters = id.filters ? `(${id.filters}) and (${companyFilters}${objectPermissionEditFilters})` : `(${companyFilters}${objectPermissionEditFilters})`;
1607
1735
  }
1608
1736
  }
1609
1737
  }
1610
1738
  else if (!objPm.modifyAllRecords && !objPm.modifyCompanyRecords && objPm.allowEdit) {
1611
1739
  if (_.isString(id)) {
1612
- id = { filters: `(_id eq \'${id}\') and (owner eq \'${userId}\')` }
1740
+ id = { filters: `(_id eq \'${id}\') and (owner eq \'${userId}\' ${objectPermissionEditFilters})` }
1613
1741
  }
1614
1742
  else if (_.isObject(id)) {
1615
1743
  if (id.filters && !_.isString(id.filters)) {
1616
1744
  id.filters = formatFiltersToODataQuery(id.filters);
1617
1745
  }
1618
- id.filters = id.filters ? `(${id.filters}) and (owner eq \'${userId}\')` : `(owner eq \'${userId}\')`;
1746
+ id.filters = id.filters ? `(${id.filters}) and (owner eq \'${userId}\' ${objectPermissionEditFilters})` : `(owner eq \'${userId}\' ${objectPermissionEditFilters})`;
1747
+ }
1748
+ } else if (objectPermissionEditFilters) {
1749
+ if (_.isString(id)) {
1750
+ id = { filters: `(_id eq \'${id}\') and (${objectPermissionEditFilters})` }
1751
+ }
1752
+ else if (_.isObject(id)) {
1753
+ if (id.filters && !_.isString(id.filters)) {
1754
+ id.filters = formatFiltersToODataQuery(id.filters);
1755
+ }
1756
+ id.filters = id.filters ? `(${id.filters}) and (${objectPermissionEditFilters})` : `(${objectPermissionEditFilters})`;
1619
1757
  }
1620
1758
  }
1621
1759
  args[args.length - 3] = id;
@@ -1639,20 +1777,37 @@ export class SteedosObjectType extends SteedosObjectProperties {
1639
1777
  }
1640
1778
  }
1641
1779
  else if (method === 'delete') {
1642
- if (!objPm.allowDelete) {
1780
+ const permissionFilters = this.getObjectPermissionFilters(objPm, userSession, true);
1781
+ if (!objPm.allowDelete && _.isEmpty(permissionFilters)) {
1643
1782
  throw new Error(`no ${method} permission!`);
1644
1783
  }
1784
+
1785
+ let objectPermissionEditFilters = '';
1786
+ if (!_.isEmpty(permissionFilters)) {
1787
+ objectPermissionEditFilters = ` or (${permissionFilters.join(' or ')})`
1788
+ }
1789
+
1645
1790
  let id = args[args.length - 2];
1646
1791
  if (!objPm.modifyAllRecords && objPm.modifyCompanyRecords) {
1647
1792
  let companyFilters = _.map(userSession.companies, function (comp: any) {
1648
1793
  return `(company_id eq '${comp._id}') or (company_ids eq '${comp._id}')`
1649
1794
  }).join(' or ')
1650
1795
  if (companyFilters) {
1651
- id = { filters: `(_id eq \'${id}\') and (${companyFilters})` };
1796
+ id = { filters: `(_id eq \'${id}\') and (${companyFilters}${objectPermissionEditFilters})` };
1652
1797
  }
1653
1798
  }
1654
1799
  else if (!objPm.modifyAllRecords && !objPm.modifyCompanyRecords) {
1655
- id = { filters: `(_id eq \'${id}\') and (owner eq \'${userId}\')` };
1800
+ id = { filters: `(_id eq \'${id}\') and (owner eq \'${userId}\'${objectPermissionEditFilters})` };
1801
+ } else if (objectPermissionEditFilters) {
1802
+ if (_.isString(id)) {
1803
+ id = { filters: `(_id eq \'${id}\') and (${objectPermissionEditFilters})` }
1804
+ }
1805
+ else if (_.isObject(id)) {
1806
+ if (id.filters && !_.isString(id.filters)) {
1807
+ id.filters = formatFiltersToODataQuery(id.filters);
1808
+ }
1809
+ id.filters = id.filters ? `(${id.filters}) and (${objectPermissionEditFilters})` : `(${objectPermissionEditFilters})`;
1810
+ }
1656
1811
  }
1657
1812
  args[args.length - 2] = id;
1658
1813
  }
@@ -22,6 +22,7 @@ abstract class SteedosObjectPermissionTypeProperties {
22
22
  unreadable_fields?: string[]
23
23
  uneditable_fields?: string[]
24
24
  unrelated_objects?: string[]
25
+ field_permissions?: any
25
26
  }
26
27
 
27
28
  export interface SteedosObjectPermissionTypeConfig extends SteedosObjectPermissionTypeProperties { }
@@ -71,6 +72,37 @@ export class SteedosObjectPermissionType extends SteedosObjectPermissionTypeProp
71
72
  this.allowRead = true;
72
73
  }
73
74
 
75
+ if (this.allowRead) {
76
+ typeof this.allowReadFiles !== "boolean" && (this.allowReadFiles = true);
77
+ typeof this.viewAllFiles !== "boolean" && (this.viewAllFiles = true);
78
+ }
79
+ if (this.allowEdit) {
80
+ typeof this.allowCreateFiles !== "boolean" && (this.allowCreateFiles = true);
81
+ typeof this.allowEditFiles !== "boolean" && (this.allowEditFiles = true);
82
+ typeof this.allowDeleteFiles !== "boolean" && (this.allowDeleteFiles = true);
83
+ }
84
+ if (this.modifyAllRecords) {
85
+ typeof this.modifyAllFiles !== "boolean" && (this.modifyAllFiles = true);
86
+ }
87
+ if (this.allowCreateFiles) {
88
+ this.allowReadFiles = true;
89
+ }
90
+ if (this.allowEditFiles) {
91
+ this.allowReadFiles = true;
92
+ }
93
+ if (this.allowDeleteFiles) {
94
+ this.allowEditFiles = true;
95
+ this.allowReadFiles = true;
96
+ }
97
+ if (this.viewAllFiles) {
98
+ this.allowReadFiles = true;
99
+ }
100
+ if (this.modifyAllFiles) {
101
+ this.allowReadFiles = true;
102
+ this.allowEditFiles = true;
103
+ this.allowDeleteFiles = true;
104
+ this.viewAllFiles = true;
105
+ }
74
106
  this.object_name = object_name
75
107
  }
76
108
 
@@ -0,0 +1,63 @@
1
+ import * as _ from 'lodash'
2
+
3
+ const globalTag = '__G_L_O_B_A_L__';
4
+
5
+ const getParentPath = function (path) {
6
+ var pathArr;
7
+ if (typeof path === 'string') {
8
+ pathArr = path.split('.');
9
+ if (pathArr.length === 1) {
10
+ return '#';
11
+ }
12
+ pathArr.pop();
13
+ return pathArr.join('.');
14
+ }
15
+ return '#';
16
+ }
17
+
18
+ const getValueByPath = function (formData, path) {
19
+ if (path === '#' || !path) {
20
+ return formData || {};
21
+ } else if (typeof path === 'string') {
22
+ return _.get(formData, path);
23
+ } else {
24
+ console.error('path has to be a string');
25
+ }
26
+ };
27
+
28
+ export const isExpression = function (func) {
29
+ var pattern, reg1, reg2;
30
+ if (typeof func !== 'string') {
31
+ return false;
32
+ }
33
+ pattern = /^{{(.+)}}$/;
34
+ reg1 = /^{{(function.+)}}$/;
35
+ reg2 = /^{{(.+=>.+)}}$/;
36
+ if (typeof func === 'string' && func.match(pattern) && !func.match(reg1) && !func.match(reg2)) {
37
+ return true;
38
+ }
39
+ return false;
40
+ };
41
+
42
+ export const parseSingleExpression = function (func, formData, dataPath, global) {
43
+ var error, funcBody, parent, parentPath, str;
44
+
45
+ if (formData === void 0) {
46
+ formData = {};
47
+ }
48
+ parentPath = getParentPath(dataPath);
49
+ parent = getValueByPath(formData, parentPath) || {};
50
+ if (typeof func === 'string') {
51
+ funcBody = func.substring(2, func.length - 2);
52
+ str = '\n return ' + funcBody.replace(/\bformData\b/g, JSON.stringify(formData).replace(/\bglobal\b/g, globalTag)).replace(/\bglobal\b/g, JSON.stringify(global)).replace(new RegExp('\\b' + globalTag + '\\b', 'g'), 'global').replace(/rootValue/g, JSON.stringify(parent));
53
+ try {
54
+ return Function(str)();
55
+ } catch (_error) {
56
+ error = _error;
57
+ console.log(error, func, dataPath);
58
+ return func;
59
+ }
60
+ } else {
61
+ return func;
62
+ }
63
+ };
package/src/util/index.ts CHANGED
@@ -27,6 +27,7 @@ export * from './permission_shares'
27
27
  export * from './suffix'
28
28
  export * from './locale'
29
29
  export * from './field'
30
+ export * from './function_expression'
30
31
 
31
32
  exports.loadJSONFile = (filePath: string)=>{
32
33
  return JSON.parse(fs.readFileSync(filePath, 'utf8').normalize('NFC'));