-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjparse.js
More file actions
159 lines (132 loc) · 4.46 KB
/
jparse.js
File metadata and controls
159 lines (132 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
const _ = require('lodash');
const { replace } = require('./replace');
const REGEX_ARRAY = /^\[.+\]$/;
const REGEX_JSON = /^\s*(\[\s*)?\{\s*("[\s\S]*"\s*:[\s\S]+)}\s*\]?\s*$/; // could be multiline
const REGEX_PATH = /^\{([^": ]+)}$/; // can't contain : and " at the start
// ==============================================
// extended JSON.parse
function parseObject(obj, src, defaultkey, replaceOptions) {
if (src === undefined || src === null || src === '') {
const res = {};
if (defaultkey) res[defaultkey] = null;
return res;
}
// already an object
if (typeof src === 'object') {
if (defaultkey
&& !Array.isArray(src)
&& !_.has(src, defaultkey)) src[defaultkey] = null;
// replace object values
replaceObjectKeysValues(obj, src);
return src;
// const dest = replace(obj, src, replaceOptions);
// return dest;
}
// pointer (path) to an object?
if (typeof src === 'string' && src.match(REGEX_PATH)) {
let srcpath = src.match(REGEX_PATH)[1];
// in case of complex paths
srcpath = replace(obj, srcpath, replaceOptions);
const srcobject = _.get(obj, srcpath);
if (typeof srcobject === 'object') {
// default key
if (defaultkey
&& !Array.isArray(srcobject)
&& !_.has(srcobject, defaultkey)) srcobject[defaultkey] = null;
// replace object values
replaceObjectKeysValues(obj, srcobject);
return srcobject;
}
}
// ok, this was not a path to object, let's check json
if (typeof src === 'string' && (src.match(REGEX_JSON) || src.match(REGEX_ARRAY))) {
let jdata;
try {
const params = { ...{ crlf: '\\n', escape: '\\"' }, ...replaceOptions };
jdata = replace(obj, src, params);
jdata = JSON.parse(jdata || '{}');
jdata = deepStringFn(jdata, (item) => item.replace(/\\n/g, '\n'));
if (defaultkey
&& !Array.isArray(jdata)
&& !_.has(jdata, defaultkey)) jdata[defaultkey] = null;
return jdata;
}
catch (err) {
// was not an object, sorry
// log.warn(`[-] parse: ${err.message}`);
highlightParseError(err, jdata);
}
}
// not an object - just a key
const res = {};
if (defaultkey === null) return res;
res[defaultkey || '_notparsed'] = replace(obj, src, replaceOptions);
return res;
}
// ==============================================
function highlightParseError(e, _string) {
const err = e.message;
const REGEX = /in JSON at position (\d+)/;
const regexResult = err.match(REGEX);
if (!regexResult) return '';
const string = _string.replace(/\n/gm, ' ');
// string = string.replace(/\t/gm, ' ');
// string = string.replace(/\r/gm, ' ');
// console.log(string);
const pos = parseInt(regexResult[1]);
// console.log(`pos ${pos}`);
// console.log(`string.length ${string.length}`);
const start = (pos <= 20) ? 0 : pos -20;
const end = (pos +20 > string.length-1) ? string.length-1 : pos +20;
// console.log(`start ${start}, end ${end}`);
const before = string.slice(start, pos).grey;
const match = string.slice(pos, pos+1).white.bgRed;
const after = string.slice(pos+1, end).grey;
// console.log(`'${before}'`);
// console.log(`'${match}'`);
// console.log(`'${after}'`);
const res = `${before}${match}${after}`;
console.warn(` parse: ${err}: ${res}`);
}
// ==============================================
function deepStringFn(obj, fn) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'string') {
obj[key] = fn(obj[key]);
return;
}
// ok, so this is an object
if (typeof obj[key] === 'object'
&& obj[key] !== null
&& obj[key] !== undefined) {
deepStringFn(obj[key], fn);
}
});
return obj;
}
// ==============================================
function replaceObjectKeysValues(obj, somedata) {
// first - change key NAMES
Object.keys(somedata).forEach((key) => {
const newkey = replace(obj, key);
if (newkey === key) return;
somedata[newkey] = somedata[key];
delete somedata[key];
});
// then change values recursive
Object.keys(somedata).forEach((key) => {
const val = somedata[key];
if (typeof val === 'object'
&& val !== null
&& val !== undefined) {
somedata[key] = replaceObjectKeysValues(obj, val);
}
else if (typeof val === 'string') {
// only replace strings (may contain placeholders)
somedata[key] = replace(obj, val);
}
// keep numbers, booleans, null as-is
});
return somedata;
}
module.exports.parse = parseObject;