@quanxiaoxiao/datav 0.1.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/.babelrc +7 -0
- package/.editorconfig +13 -0
- package/.eslintrc +29 -0
- package/package.json +35 -0
- package/src/checkout.mjs +134 -0
- package/src/checkout.test.mjs +140 -0
- package/src/getValueOfPathname.mjs +44 -0
- package/src/getValueOfPathname.test.mjs +29 -0
- package/src/index.mjs +9 -0
- package/src/select/check.mjs +63 -0
- package/src/select/check.test.mjs +75 -0
- package/src/select/index.mjs +108 -0
- package/src/select/index.test.mjs +742 -0
package/.babelrc
ADDED
package/.editorconfig
ADDED
package/.eslintrc
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@babel/eslint-parser",
|
|
3
|
+
"extends": "eslint-config-airbnb",
|
|
4
|
+
"env": {
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"rules": {
|
|
8
|
+
"no-console": 0,
|
|
9
|
+
"max-len": 0,
|
|
10
|
+
"no-continue": 0,
|
|
11
|
+
"no-bitwise": 0,
|
|
12
|
+
"import/no-extraneous-dependencies": [
|
|
13
|
+
"error",
|
|
14
|
+
{
|
|
15
|
+
"devDependencies": true,
|
|
16
|
+
"optionalDependencies": false,
|
|
17
|
+
"peerDependencies": false
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"no-param-reassign": 0,
|
|
21
|
+
"no-mixed-operators": 0,
|
|
22
|
+
"no-underscore-dangle": 0,
|
|
23
|
+
"import/prefer-default-export": 0,
|
|
24
|
+
"class-methods-use-this": 0,
|
|
25
|
+
"no-plusplus": 0,
|
|
26
|
+
"global-require": 0
|
|
27
|
+
},
|
|
28
|
+
"globals": {}
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quanxiaoxiao/datav",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "node --test"
|
|
7
|
+
},
|
|
8
|
+
"main": "./src/index.mjs",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/index.mjs",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@babel/eslint-parser": "^7.23.10",
|
|
15
|
+
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
|
|
16
|
+
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
|
17
|
+
"@babel/plugin-syntax-import-attributes": "^7.23.3",
|
|
18
|
+
"eslint": "^8.56.0",
|
|
19
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
20
|
+
"eslint-plugin-import": "^2.29.1",
|
|
21
|
+
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
22
|
+
"eslint-plugin-react": "^7.33.2",
|
|
23
|
+
"eslint-plugin-react-hooks": "^4.6.0"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"registry": "https://registry.npmjs.org"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">= 20.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"ajv": "^8.12.0",
|
|
33
|
+
"lodash": "^4.17.21"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/checkout.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
|
|
3
|
+
const DATA_TYPE_NUMBER = 'number';
|
|
4
|
+
const DATA_TYPE_STRING = 'string';
|
|
5
|
+
const DATA_TYPE_BOOLEAN = 'boolean';
|
|
6
|
+
const DATA_TYPE_JSON = 'json';
|
|
7
|
+
const DATA_TYPE_ARRAY = 'array';
|
|
8
|
+
const DATA_TYPE_OBJECT = 'object';
|
|
9
|
+
const DATA_TYPE_INTEGER = 'integer';
|
|
10
|
+
|
|
11
|
+
const map = {
|
|
12
|
+
[DATA_TYPE_STRING]: (v) => {
|
|
13
|
+
if (typeof v !== 'string') {
|
|
14
|
+
return v.toString ? `${v.toString()}` : JSON.stringify(v);
|
|
15
|
+
}
|
|
16
|
+
return v;
|
|
17
|
+
},
|
|
18
|
+
[DATA_TYPE_INTEGER]: (v) => {
|
|
19
|
+
if (Number.isNaN(v)) {
|
|
20
|
+
return v;
|
|
21
|
+
}
|
|
22
|
+
if (v === '') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const type = typeof v;
|
|
26
|
+
if (type !== 'number' && type !== 'string') {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const number = Number(v);
|
|
30
|
+
if (Number.isNaN(number)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (`${number}` !== `${v}`) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return parseInt(number, 10);
|
|
37
|
+
},
|
|
38
|
+
[DATA_TYPE_NUMBER]: (v) => {
|
|
39
|
+
if (v === '') {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const number = Number(v);
|
|
43
|
+
if (Number.isNaN(number)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (`${number}` !== `${v}`) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return number;
|
|
50
|
+
},
|
|
51
|
+
[DATA_TYPE_BOOLEAN]: (v) => {
|
|
52
|
+
if (v !== 'false' && v !== 'true') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return v === 'true';
|
|
56
|
+
},
|
|
57
|
+
[DATA_TYPE_JSON]: (v) => {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(v);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[DATA_TYPE_OBJECT]: (v) => {
|
|
65
|
+
try {
|
|
66
|
+
const d = JSON.parse(v);
|
|
67
|
+
if (Array.isArray(d)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (typeof d !== 'object') {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return d;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[DATA_TYPE_ARRAY]: (v) => {
|
|
79
|
+
try {
|
|
80
|
+
const d = JSON.parse(v);
|
|
81
|
+
if (Array.isArray(d)) {
|
|
82
|
+
return d;
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const typeNameMap = {
|
|
92
|
+
[DATA_TYPE_NUMBER]: 'number',
|
|
93
|
+
[DATA_TYPE_STRING]: 'string',
|
|
94
|
+
[DATA_TYPE_INTEGER]: 'integer',
|
|
95
|
+
[DATA_TYPE_BOOLEAN]: 'boolean',
|
|
96
|
+
[DATA_TYPE_JSON]: 'object',
|
|
97
|
+
[DATA_TYPE_ARRAY]: 'object',
|
|
98
|
+
[DATA_TYPE_OBJECT]: 'object',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default (value, type) => {
|
|
102
|
+
if (type == null) {
|
|
103
|
+
throw new Error('data type is empty');
|
|
104
|
+
}
|
|
105
|
+
if (!Object.hasOwnProperty.call(map, type)) {
|
|
106
|
+
throw new Error(`\`${type}\` invalid data type`);
|
|
107
|
+
}
|
|
108
|
+
if (value == null) {
|
|
109
|
+
if (type === 'array') {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const valueType = typeof value;
|
|
115
|
+
if (valueType !== 'string') {
|
|
116
|
+
if (type === DATA_TYPE_INTEGER) {
|
|
117
|
+
return map[DATA_TYPE_INTEGER](value);
|
|
118
|
+
}
|
|
119
|
+
if (type === DATA_TYPE_STRING) {
|
|
120
|
+
return map[DATA_TYPE_STRING](value);
|
|
121
|
+
}
|
|
122
|
+
if (valueType === typeNameMap[type]) {
|
|
123
|
+
if (type === DATA_TYPE_ARRAY) {
|
|
124
|
+
return Array.isArray(value) ? value : [];
|
|
125
|
+
}
|
|
126
|
+
if (type === DATA_TYPE_OBJECT) {
|
|
127
|
+
return _.isPlainObject(value) ? value : null;
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
return type === DATA_TYPE_ARRAY ? [] : null;
|
|
132
|
+
}
|
|
133
|
+
return map[type](value);
|
|
134
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import checkout from './checkout.mjs';
|
|
4
|
+
|
|
5
|
+
test('checkout invalid data type', () => {
|
|
6
|
+
assert.throws(() => {
|
|
7
|
+
checkout('aaa', 'bbb');
|
|
8
|
+
});
|
|
9
|
+
assert.throws(() => {
|
|
10
|
+
checkout('aaa');
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('checkout with data value null', () => {
|
|
15
|
+
assert.deepEqual(checkout(null, 'array'), []);
|
|
16
|
+
assert.equal(checkout(null, 'object'), null);
|
|
17
|
+
assert.equal(checkout(null, 'string'), null);
|
|
18
|
+
assert.equal(checkout(null, 'number'), null);
|
|
19
|
+
assert.equal(checkout(null, 'integer'), null);
|
|
20
|
+
assert.equal(checkout(null, 'boolean'), null);
|
|
21
|
+
assert.equal(checkout(null, 'json'), null);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('checkout with string', () => {
|
|
25
|
+
assert.equal(checkout(1, 'string'), '1');
|
|
26
|
+
assert.equal(checkout(null, 'string'), null);
|
|
27
|
+
assert.equal(checkout(true, 'string'), 'true');
|
|
28
|
+
assert.equal(checkout(false, 'string'), 'false');
|
|
29
|
+
assert.equal(checkout(' 1', 'string'), ' 1');
|
|
30
|
+
assert.equal(checkout([1, 2, 3], 'string'), '1,2,3');
|
|
31
|
+
assert.equal(checkout({ name: 'cqq' }, 'string'), '[object Object]');
|
|
32
|
+
assert.equal(checkout({
|
|
33
|
+
name: 'quan',
|
|
34
|
+
toString: () => 'cqq',
|
|
35
|
+
}, 'string'), 'cqq');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('checkout with integer', () => {
|
|
39
|
+
assert.equal(checkout(null, 'integer'), null);
|
|
40
|
+
assert.equal(checkout('', 'integer'), null);
|
|
41
|
+
assert.equal(checkout(true, 'integer'), null);
|
|
42
|
+
assert.equal(checkout(false, 'integer'), null);
|
|
43
|
+
assert.equal(checkout([], 'integer'), null);
|
|
44
|
+
assert.equal(checkout({}, 'integer'), null);
|
|
45
|
+
assert.equal(checkout('aaa', 'integer'), null);
|
|
46
|
+
assert.equal(checkout('1', 'integer'), 1);
|
|
47
|
+
assert.equal(checkout('01', 'integer'), null);
|
|
48
|
+
assert.equal(checkout(' 1', 'integer'), null);
|
|
49
|
+
assert.equal(checkout('1.1', 'integer'), 1);
|
|
50
|
+
assert.equal(checkout('-3.1', 'integer'), -3);
|
|
51
|
+
assert.equal(checkout(3.1, 'integer'), 3);
|
|
52
|
+
assert.equal(checkout(1, 'integer'), 1);
|
|
53
|
+
assert(Number.isNaN(checkout(NaN, 'integer')));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('checkout with number', () => {
|
|
57
|
+
assert.equal(checkout('1', 'number'), 1);
|
|
58
|
+
assert.equal(checkout('01', 'number'), null);
|
|
59
|
+
assert.equal(checkout(true, 'number'), null);
|
|
60
|
+
assert.equal(checkout(false, 'number'), null);
|
|
61
|
+
assert.equal(checkout([], 'number'), null);
|
|
62
|
+
assert.equal(checkout({}, 'number'), null);
|
|
63
|
+
assert.equal(checkout('', 'number'), null);
|
|
64
|
+
assert.equal(checkout('a', 'number'), null);
|
|
65
|
+
assert.equal(checkout('1a', 'number'), null);
|
|
66
|
+
assert.equal(checkout('0', 'number'), 0);
|
|
67
|
+
assert.equal(checkout('-0', 'number'), null);
|
|
68
|
+
assert.equal(checkout('-1', 'number'), -1);
|
|
69
|
+
assert.equal(checkout('-1.5', 'number'), -1.5);
|
|
70
|
+
assert.equal(checkout('-2.5', 'number'), -2.5);
|
|
71
|
+
assert.equal(checkout('2.5', 'number'), 2.5);
|
|
72
|
+
assert.equal(checkout(1, 'number'), 1);
|
|
73
|
+
assert(Number.isNaN(checkout(NaN, 'number')));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('checkout with boolean', () => {
|
|
77
|
+
assert.equal(checkout('', 'boolean'), null);
|
|
78
|
+
assert.equal(checkout('false', 'boolean'), false);
|
|
79
|
+
assert.equal(checkout(' false', 'boolean'), null);
|
|
80
|
+
assert.equal(checkout('false ', 'boolean'), null);
|
|
81
|
+
assert.equal(checkout('true', 'boolean'), true);
|
|
82
|
+
assert.equal(checkout(' true', 'boolean'), null);
|
|
83
|
+
assert.equal(checkout('true ', 'boolean'), null);
|
|
84
|
+
assert.equal(checkout(true, 'boolean'), true);
|
|
85
|
+
assert.equal(checkout(false, 'boolean'), false);
|
|
86
|
+
assert.equal(checkout(1, 'boolean'), null);
|
|
87
|
+
assert.equal(checkout({}, 'boolean'), null);
|
|
88
|
+
assert.equal(checkout([], 'boolean'), null);
|
|
89
|
+
assert.equal(checkout('aaa', 'boolean'), null);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('checkout with json', () => {
|
|
93
|
+
assert.equal(checkout('1', 'json'), 1);
|
|
94
|
+
assert.equal(checkout(' 1', 'json'), 1);
|
|
95
|
+
assert.equal(checkout('"1"', 'json'), '1');
|
|
96
|
+
assert.equal(checkout('\'1\'', 'json'), null);
|
|
97
|
+
assert.equal(checkout('null', 'json'), null);
|
|
98
|
+
assert.equal(checkout('aa', 'json'), null);
|
|
99
|
+
assert.deepEqual(checkout('{}', 'json'), {});
|
|
100
|
+
assert.deepEqual(checkout('{fail}', 'json'), null);
|
|
101
|
+
assert.deepEqual(checkout('{"name":"cqq"}', 'json'), { name: 'cqq' });
|
|
102
|
+
assert.deepEqual(checkout('[]', 'json'), []);
|
|
103
|
+
assert.deepEqual(checkout([], 'json'), []);
|
|
104
|
+
assert.deepEqual(checkout({}, 'json'), {});
|
|
105
|
+
assert.deepEqual(checkout(2, 'json'), null);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('checkout with array', () => {
|
|
109
|
+
assert.deepEqual(checkout(null, 'array'), []);
|
|
110
|
+
assert.deepEqual(checkout('[]', 'array'), []);
|
|
111
|
+
assert.deepEqual(checkout('[xxx]', 'array'), []);
|
|
112
|
+
assert.deepEqual(checkout([], 'array'), []);
|
|
113
|
+
assert.deepEqual(checkout([1, 2, 3], 'array'), [1, 2, 3]);
|
|
114
|
+
assert.deepEqual(checkout(1, 'array'), []);
|
|
115
|
+
assert.deepEqual(checkout({}, 'array'), []);
|
|
116
|
+
assert.deepEqual(checkout('1', 'array'), []);
|
|
117
|
+
assert.deepEqual(checkout('{}', 'array'), []);
|
|
118
|
+
assert.deepEqual(checkout(['12345'], 'array'), ['12345']);
|
|
119
|
+
assert.deepEqual(checkout(true, 'array'), []);
|
|
120
|
+
assert.deepEqual(checkout(false, 'array'), []);
|
|
121
|
+
assert.deepEqual(checkout([{ name: 'cqq' }], 'array'), [{ name: 'cqq' }]);
|
|
122
|
+
assert.deepEqual(checkout(JSON.stringify([{ name: 'cqq' }]), 'array'), [{ name: 'cqq' }]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('checkout with object', () => {
|
|
126
|
+
assert.equal(checkout(null, 'object'), null);
|
|
127
|
+
assert.equal(checkout(1, 'object'), null);
|
|
128
|
+
assert.equal(checkout('aa', 'object'), null);
|
|
129
|
+
assert.equal(checkout('1', 'object'), null);
|
|
130
|
+
assert.equal(checkout(JSON.stringify('aa'), 'object'), null);
|
|
131
|
+
assert.equal(checkout(true, 'object'), null);
|
|
132
|
+
assert.equal(checkout('true', 'object'), null);
|
|
133
|
+
assert.equal(checkout('false', 'object'), null);
|
|
134
|
+
assert.equal(checkout(false, 'object'), null);
|
|
135
|
+
assert.equal(checkout([], 'object'), null);
|
|
136
|
+
assert.equal(checkout(JSON.stringify([]), 'object'), null);
|
|
137
|
+
assert.deepEqual(checkout({ name: 'cqq' }, 'object'), { name: 'cqq' });
|
|
138
|
+
assert.deepEqual(checkout(JSON.stringify({ name: 'cqq' }), 'object'), { name: 'cqq' });
|
|
139
|
+
assert.deepEqual(checkout('{fail}', 'object'), null);
|
|
140
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
|
|
3
|
+
const walk = (obj, nameList) => {
|
|
4
|
+
const [dataKey, ...other] = nameList;
|
|
5
|
+
if (Array.isArray(obj)) {
|
|
6
|
+
const n = parseInt(dataKey, 10);
|
|
7
|
+
if (Number.isNaN(n) || `${n}` !== dataKey) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const len = obj.length;
|
|
11
|
+
if (n > len) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (other.length === 0) {
|
|
15
|
+
return obj[n];
|
|
16
|
+
}
|
|
17
|
+
return walk(obj[n], other);
|
|
18
|
+
}
|
|
19
|
+
if (!Object.hasOwnProperty.call(obj, dataKey)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const value = obj[dataKey];
|
|
23
|
+
if (other.length === 0) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
return walk(value, other);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default (obj, pathname) => {
|
|
30
|
+
if (typeof pathname !== 'string' || (/\.$/.test(pathname) && pathname !== '.')) {
|
|
31
|
+
throw new Error('pathname invalid');
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(obj) && !_.isPlainObject(obj)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
let str = pathname;
|
|
37
|
+
if (pathname.startsWith('.')) {
|
|
38
|
+
str = pathname.slice(1);
|
|
39
|
+
}
|
|
40
|
+
if (str === '') {
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
return walk(obj, str.split(/(?<!\\)\./).map((d) => d.replace(/\\\./g, '.')));
|
|
44
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import getValueOfPathname from './getValueOfPathname.mjs';
|
|
4
|
+
|
|
5
|
+
test('getValueOfPathname', () => {
|
|
6
|
+
assert.throws(() => {
|
|
7
|
+
getValueOfPathname({}, 11);
|
|
8
|
+
});
|
|
9
|
+
assert.throws(() => {
|
|
10
|
+
getValueOfPathname({}, 'name.');
|
|
11
|
+
});
|
|
12
|
+
assert.deepEqual(getValueOfPathname({}, '.'), {});
|
|
13
|
+
assert.equal(getValueOfPathname([], 'name'), null);
|
|
14
|
+
assert.equal(getValueOfPathname(null, 'xxx'), null);
|
|
15
|
+
assert.equal(getValueOfPathname({ name: 'quan' }, 'quan'), null);
|
|
16
|
+
assert.equal(getValueOfPathname({ name: 'quan' }, 'name'), 'quan');
|
|
17
|
+
assert.equal(getValueOfPathname({ name: 'quan' }, '.name'), 'quan');
|
|
18
|
+
assert.equal(getValueOfPathname({ '.name': 'quan', name: 'cqq' }, '\\.name'), 'quan');
|
|
19
|
+
assert.deepEqual(getValueOfPathname({ name: 'quan' }, ''), { name: 'quan' });
|
|
20
|
+
assert.deepEqual(getValueOfPathname({ name: 'quan', obj: { foo: 'bar' } }, 'obj'), { foo: 'bar' });
|
|
21
|
+
assert.equal(getValueOfPathname({ name: 'quan', obj: { name: 'bar' } }, 'obj.name'), 'bar');
|
|
22
|
+
assert.equal(getValueOfPathname({ 'obj.name': 'xxx', name: 'quan', obj: { name: 'bar' } }, 'obj\\.name'), 'xxx');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('getValueOfPathname of array', () => {
|
|
26
|
+
assert.equal(getValueOfPathname([{ name: 'aa' }, { name: 'bb' }], '0.name'), 'aa');
|
|
27
|
+
assert.equal(getValueOfPathname([{ name: 'aa' }, { name: 'bb' }], '5.name'), null);
|
|
28
|
+
assert.equal(getValueOfPathname([{ name: 'aa' }, { name: 'bb' }], '5'), null);
|
|
29
|
+
});
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
|
|
3
|
+
const ajv = new Ajv();
|
|
4
|
+
|
|
5
|
+
const validate = ajv.compile({
|
|
6
|
+
type: 'object',
|
|
7
|
+
anyOf: [
|
|
8
|
+
{
|
|
9
|
+
properties: {
|
|
10
|
+
type: {
|
|
11
|
+
enum: ['object'],
|
|
12
|
+
},
|
|
13
|
+
properties: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
required: ['type', 'properties'],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
properties: {
|
|
21
|
+
type: {
|
|
22
|
+
enum: ['array'],
|
|
23
|
+
},
|
|
24
|
+
properties: {
|
|
25
|
+
anyOf: [
|
|
26
|
+
{
|
|
27
|
+
type: 'object',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: [
|
|
32
|
+
{
|
|
33
|
+
type: 'string',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'object',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
additionalItems: false,
|
|
40
|
+
minItems: 2,
|
|
41
|
+
maxItems: 2,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['type', 'properties'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
properties: {
|
|
50
|
+
type: {
|
|
51
|
+
enum: ['string', 'number', 'boolean', 'integer'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ['type'],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export default (express) => {
|
|
60
|
+
if (!validate(express)) {
|
|
61
|
+
throw new Error(`\`${JSON.stringify(express)}\` ${JSON.stringify(validate.errors)}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import check from './check.mjs';
|
|
4
|
+
|
|
5
|
+
test('select > check', () => {
|
|
6
|
+
assert.throws(() => {
|
|
7
|
+
check([]);
|
|
8
|
+
});
|
|
9
|
+
assert.throws(() => {
|
|
10
|
+
check('number');
|
|
11
|
+
});
|
|
12
|
+
assert.throws(() => {
|
|
13
|
+
check('integer');
|
|
14
|
+
});
|
|
15
|
+
assert.throws(() => {
|
|
16
|
+
check({});
|
|
17
|
+
});
|
|
18
|
+
assert.throws(() => {
|
|
19
|
+
check({
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: [],
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
assert.throws(() => {
|
|
25
|
+
check({
|
|
26
|
+
type: 'xxx',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
assert.throws(() => {
|
|
30
|
+
check({
|
|
31
|
+
type: 'json',
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
assert.throws(() => {
|
|
35
|
+
check({
|
|
36
|
+
type: 'array',
|
|
37
|
+
properties: [],
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
assert.throws(() => {
|
|
41
|
+
check({
|
|
42
|
+
type: 'array',
|
|
43
|
+
properties: ['dataKey', []],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
check({
|
|
47
|
+
type: 'string',
|
|
48
|
+
});
|
|
49
|
+
check({
|
|
50
|
+
type: 'string',
|
|
51
|
+
properties: ['dataKey'],
|
|
52
|
+
});
|
|
53
|
+
check({
|
|
54
|
+
type: 'string',
|
|
55
|
+
properties: ['dataKey', []],
|
|
56
|
+
});
|
|
57
|
+
check({
|
|
58
|
+
type: 'string',
|
|
59
|
+
properties: ['dataKey', {}],
|
|
60
|
+
});
|
|
61
|
+
check({
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
check({
|
|
67
|
+
type: 'array',
|
|
68
|
+
properties: {
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
check({
|
|
72
|
+
type: 'array',
|
|
73
|
+
properties: ['dataKey', {}],
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* eslint no-use-before-define: 0 */
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import check from './check.mjs';
|
|
4
|
+
import checkout from '../checkout.mjs';
|
|
5
|
+
import getValueOfPathname from '../getValueOfPathname.mjs';
|
|
6
|
+
|
|
7
|
+
function walkWithObject(properties) {
|
|
8
|
+
const keys = Object.keys(properties);
|
|
9
|
+
const list = [];
|
|
10
|
+
for (let i = 0; i < keys.length; i++) {
|
|
11
|
+
const dataKey = keys[i];
|
|
12
|
+
const express = properties[dataKey];
|
|
13
|
+
const handler = {
|
|
14
|
+
dataKey,
|
|
15
|
+
express,
|
|
16
|
+
fn: select(express),
|
|
17
|
+
};
|
|
18
|
+
list.push(handler);
|
|
19
|
+
}
|
|
20
|
+
return (d, _root) => {
|
|
21
|
+
const root = _root == null ? d : _root;
|
|
22
|
+
return list.reduce((acc, cur) => {
|
|
23
|
+
if (Array.isArray(cur.express)) {
|
|
24
|
+
return {
|
|
25
|
+
...acc,
|
|
26
|
+
[cur.dataKey]: cur.fn(d, root),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
...acc,
|
|
31
|
+
[cur.dataKey]: cur.fn(d == null ? d : d[cur.dataKey], root),
|
|
32
|
+
};
|
|
33
|
+
}, {});
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function select(express) {
|
|
38
|
+
if (Array.isArray(express)) {
|
|
39
|
+
const [pathname] = express;
|
|
40
|
+
if (typeof pathname !== 'string'
|
|
41
|
+
|| !_.isPlainObject(express[1])
|
|
42
|
+
) {
|
|
43
|
+
throw new Error(`\`${JSON.stringify(express)}\` express invalid`);
|
|
44
|
+
}
|
|
45
|
+
const walk = select(express[1]);
|
|
46
|
+
return (obj, _root) => {
|
|
47
|
+
const root = _root == null ? obj : _root;
|
|
48
|
+
if (pathname.startsWith('$')) {
|
|
49
|
+
return walk(getValueOfPathname(root, pathname.slice(1)), root);
|
|
50
|
+
}
|
|
51
|
+
return walk(getValueOfPathname(obj, pathname), root);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
check(express);
|
|
55
|
+
if (['string', 'number', 'boolean', 'integer'].includes(express.type)) {
|
|
56
|
+
return (v, _root) => {
|
|
57
|
+
const root = _root == null ? v : _root;
|
|
58
|
+
let value = v;
|
|
59
|
+
if (express.resolve) {
|
|
60
|
+
value = express.resolve(value, root);
|
|
61
|
+
}
|
|
62
|
+
return checkout(value, express.type);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (express.resolve) {
|
|
66
|
+
console.warn('data type `array` or `object` unspport resolve');
|
|
67
|
+
}
|
|
68
|
+
if (express.type === 'object') {
|
|
69
|
+
return walkWithObject(express.properties);
|
|
70
|
+
}
|
|
71
|
+
if (Array.isArray(express.properties)) {
|
|
72
|
+
const walk = select(express.properties[1]);
|
|
73
|
+
return (arr, _root) => {
|
|
74
|
+
const root = _root == null ? arr : _root;
|
|
75
|
+
const [pathname] = express.properties;
|
|
76
|
+
if (!Array.isArray(arr)) {
|
|
77
|
+
if (pathname.startsWith('$')) {
|
|
78
|
+
const ret = walk(getValueOfPathname(root, pathname.slice(1)), root);
|
|
79
|
+
if (ret == null) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return [ret];
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return arr.map((d) => {
|
|
87
|
+
if (pathname === '' || pathname === '.') {
|
|
88
|
+
return walk(d, root);
|
|
89
|
+
}
|
|
90
|
+
return walk(getValueOfPathname(d, pathname), root);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const walk = walkWithObject(express.properties);
|
|
95
|
+
return (arr, _root) => {
|
|
96
|
+
const root = _root == null ? arr : _root;
|
|
97
|
+
if (!Array.isArray(arr)) {
|
|
98
|
+
if (_.isEmpty(express.properties)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const ret = walk(arr, root);
|
|
102
|
+
return [ret];
|
|
103
|
+
}
|
|
104
|
+
return arr.map((d) => walk(d, root));
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default select;
|