@bhsd/common 1.1.0 → 1.2.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/dist/index.d.ts +15 -3
- package/dist/index.js +75 -8
- package/dist/index.mjs +333 -15
- package/dist/json_parse.js +328 -0
- package/package.json +9 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
declare interface JsonError {
|
|
2
|
+
severity: 'error' | 'warning';
|
|
2
3
|
message: string;
|
|
3
|
-
line: number
|
|
4
|
-
column: number
|
|
5
|
-
position: number
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
position: number;
|
|
6
7
|
}
|
|
7
8
|
export type RegexGetter<T = string> = (s: T) => RegExp;
|
|
8
9
|
export declare const wmf = "wiktionary|wiki(?:pedia|books|news|quote|source|versity|voyage)";
|
|
@@ -39,6 +40,17 @@ export declare function getRegex<T extends object>(f: RegexGetter<T>): RegexGett
|
|
|
39
40
|
* @deprecated 需要改为使用`getRegex`
|
|
40
41
|
*/
|
|
41
42
|
export declare const getObjRegex: typeof getRegex;
|
|
43
|
+
/**
|
|
44
|
+
* 按行分割字符串并记录每行的起止位置
|
|
45
|
+
* @param str 字符串
|
|
46
|
+
*/
|
|
47
|
+
export declare const splitLines: (str: string) => [string, number, number][];
|
|
48
|
+
/**
|
|
49
|
+
* 使用`JSON.parse()`诊断JSON字符串中的语法错误
|
|
50
|
+
* @param str JSON字符串
|
|
51
|
+
* @param force 是否强制诊断
|
|
52
|
+
*/
|
|
53
|
+
export declare const lintJSONNative: (str: string, force?: boolean) => JsonError[];
|
|
42
54
|
/**
|
|
43
55
|
* 诊断JSON字符串中的语法错误
|
|
44
56
|
* @param str JSON字符串
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.lintJSON = exports.getObjRegex = exports.sanitizeInlineStyle = exports.splitColors = exports.numToHex = exports.rawurldecode = exports.wmf = void 0;
|
|
3
|
+
exports.lintJSON = exports.lintJSONNative = exports.splitLines = exports.getObjRegex = exports.sanitizeInlineStyle = exports.splitColors = exports.numToHex = exports.rawurldecode = exports.wmf = void 0;
|
|
4
4
|
exports.getRegex = getRegex;
|
|
5
|
+
const json_parse_js_1 = require("./json_parse.js");
|
|
5
6
|
exports.wmf = 'wiktionary|wiki(?:pedia|books|news|quote|source|versity|voyage)';
|
|
6
7
|
/**
|
|
7
8
|
* PHP的`rawurldecode`函数的JavaScript实现
|
|
@@ -72,23 +73,89 @@ function getRegex(f) {
|
|
|
72
73
|
* @deprecated 需要改为使用`getRegex`
|
|
73
74
|
*/
|
|
74
75
|
exports.getObjRegex = getRegex;
|
|
76
|
+
/**
|
|
77
|
+
* 按行分割字符串并记录每行的起止位置
|
|
78
|
+
* @param str 字符串
|
|
79
|
+
*/
|
|
80
|
+
const splitLines = (str) => {
|
|
81
|
+
const lines = [];
|
|
82
|
+
let start = 0;
|
|
83
|
+
for (const line of str.split('\n')) {
|
|
84
|
+
const end = start + line.length;
|
|
85
|
+
lines.push([line, start, end]);
|
|
86
|
+
start = end + 1;
|
|
87
|
+
}
|
|
88
|
+
return lines;
|
|
89
|
+
};
|
|
90
|
+
exports.splitLines = splitLines;
|
|
75
91
|
const mt2num = (mt) => mt && Number(mt[1]);
|
|
92
|
+
const formatJsonError = (str, errors) => {
|
|
93
|
+
let lines;
|
|
94
|
+
for (const error of errors) {
|
|
95
|
+
const { line, column, position } = error;
|
|
96
|
+
if (position === null || position === undefined) {
|
|
97
|
+
if (line) {
|
|
98
|
+
lines ??= (0, exports.splitLines)(str);
|
|
99
|
+
error.column ??= 1;
|
|
100
|
+
error.position = lines[line - 1][1] + (error.column - 1);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
error.position = 0;
|
|
104
|
+
error.line = 1;
|
|
105
|
+
error.column = 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (!line || !column) {
|
|
109
|
+
lines ??= (0, exports.splitLines)(str);
|
|
110
|
+
error.line = lines.findIndex(([, , end]) => position <= end) + 1;
|
|
111
|
+
error.column = position - lines[error.line - 1][1] + 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* 使用`JSON.parse()`诊断JSON字符串中的语法错误
|
|
118
|
+
* @param str JSON字符串
|
|
119
|
+
* @param force 是否强制诊断
|
|
120
|
+
*/
|
|
121
|
+
const lintJSONNative = (str, force) => {
|
|
122
|
+
if (force || str.trim()) {
|
|
123
|
+
try {
|
|
124
|
+
JSON.parse(str);
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
const { message } = e, line = mt2num(/\bline (\d+)/u.exec(message)), column = mt2num(/\bcolumn (\d+)/u.exec(message)), position = mt2num(/\bposition (\d+)/u.exec(message));
|
|
128
|
+
return formatJsonError(str, [{ message, line, column, position, severity: 'error' }]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
};
|
|
133
|
+
exports.lintJSONNative = lintJSONNative;
|
|
76
134
|
/**
|
|
77
135
|
* 诊断JSON字符串中的语法错误
|
|
78
136
|
* @param str JSON字符串
|
|
79
137
|
*/
|
|
80
138
|
const lintJSON = (str) => {
|
|
139
|
+
if (!str.trim()) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
let errors;
|
|
81
143
|
try {
|
|
82
|
-
|
|
83
|
-
JSON.parse(str);
|
|
84
|
-
}
|
|
144
|
+
(0, json_parse_js_1.json_parse)(str);
|
|
85
145
|
}
|
|
86
146
|
catch (e) {
|
|
87
|
-
if (e instanceof
|
|
88
|
-
const {
|
|
89
|
-
|
|
147
|
+
if (!(e instanceof Error)) {
|
|
148
|
+
const { warnings, ...error } = e;
|
|
149
|
+
if (error.message) {
|
|
150
|
+
warnings.push(error);
|
|
151
|
+
}
|
|
152
|
+
errors = formatJsonError(str, warnings);
|
|
153
|
+
if (error.message) {
|
|
154
|
+
return errors;
|
|
155
|
+
}
|
|
90
156
|
}
|
|
91
157
|
}
|
|
92
|
-
|
|
158
|
+
const nativeErrors = (0, exports.lintJSONNative)(str);
|
|
159
|
+
return errors ? [...nativeErrors, ...errors] : nativeErrors;
|
|
93
160
|
};
|
|
94
161
|
exports.lintJSON = lintJSON;
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,278 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// src/json_parse.ts
|
|
2
|
+
var json_parse = /* @__PURE__ */ (() => {
|
|
3
|
+
let at;
|
|
4
|
+
let ch;
|
|
5
|
+
const escapee = {
|
|
6
|
+
'"': '"',
|
|
7
|
+
"\\": "\\",
|
|
8
|
+
"/": "/",
|
|
9
|
+
b: "\b",
|
|
10
|
+
f: "\f",
|
|
11
|
+
n: "\n",
|
|
12
|
+
r: "\r",
|
|
13
|
+
t: " "
|
|
14
|
+
};
|
|
15
|
+
const spaces = /* @__PURE__ */ new Set([" ", " ", "\n", "\r"]);
|
|
16
|
+
let text;
|
|
17
|
+
let warnings;
|
|
18
|
+
const stringify = (c) => {
|
|
19
|
+
if (c === "") {
|
|
20
|
+
return "end of input";
|
|
21
|
+
}
|
|
22
|
+
return c === '"' ? `'"'` : JSON.stringify(c);
|
|
23
|
+
};
|
|
24
|
+
const warn = (m) => {
|
|
25
|
+
warnings.push({
|
|
26
|
+
message: m,
|
|
27
|
+
position: at - 1,
|
|
28
|
+
severity: "warning"
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const error = (m) => {
|
|
32
|
+
throw {
|
|
33
|
+
warnings,
|
|
34
|
+
message: m,
|
|
35
|
+
position: at - 1,
|
|
36
|
+
severity: "error"
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const next = (c) => {
|
|
40
|
+
if (c && c !== ch) {
|
|
41
|
+
error(`Expected ${stringify(c)} instead of ${stringify(ch)}`);
|
|
42
|
+
}
|
|
43
|
+
ch = text.charAt(at);
|
|
44
|
+
at += 1;
|
|
45
|
+
return ch;
|
|
46
|
+
};
|
|
47
|
+
const number = () => {
|
|
48
|
+
let value2;
|
|
49
|
+
let string2 = "";
|
|
50
|
+
if (ch === "-") {
|
|
51
|
+
string2 = "-";
|
|
52
|
+
next();
|
|
53
|
+
}
|
|
54
|
+
if (ch === "0") {
|
|
55
|
+
string2 += ch;
|
|
56
|
+
next();
|
|
57
|
+
if (ch >= "0" && ch <= "9") {
|
|
58
|
+
error("Bad number");
|
|
59
|
+
}
|
|
60
|
+
} else if (ch >= "1" && ch <= "9") {
|
|
61
|
+
while (ch >= "0" && ch <= "9") {
|
|
62
|
+
string2 += ch;
|
|
63
|
+
next();
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
error("No number after minus sign");
|
|
67
|
+
}
|
|
68
|
+
if (ch !== "." && ch !== "e" && ch !== "E") {
|
|
69
|
+
value2 = Number(string2);
|
|
70
|
+
if (!Number.isSafeInteger(value2)) {
|
|
71
|
+
warn("Not a safe integer");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (ch === ".") {
|
|
75
|
+
string2 += ".";
|
|
76
|
+
next();
|
|
77
|
+
if (ch < "0" || ch > "9") {
|
|
78
|
+
error("Unterminated fractional number");
|
|
79
|
+
}
|
|
80
|
+
while (ch >= "0" && ch <= "9") {
|
|
81
|
+
string2 += ch;
|
|
82
|
+
next();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (ch === "e" || ch === "E") {
|
|
86
|
+
string2 += ch;
|
|
87
|
+
next();
|
|
88
|
+
if (ch === "-" || ch === "+") {
|
|
89
|
+
string2 += ch;
|
|
90
|
+
next();
|
|
91
|
+
}
|
|
92
|
+
while (ch >= "0" && ch <= "9") {
|
|
93
|
+
string2 += ch;
|
|
94
|
+
next();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
value2 ??= Number(string2);
|
|
98
|
+
if (!Number.isFinite(value2)) {
|
|
99
|
+
error("Bad number");
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const string = () => {
|
|
103
|
+
let hex;
|
|
104
|
+
let i;
|
|
105
|
+
let value2 = "";
|
|
106
|
+
let uffff;
|
|
107
|
+
if (ch === '"') {
|
|
108
|
+
while (next()) {
|
|
109
|
+
if (ch === '"') {
|
|
110
|
+
next();
|
|
111
|
+
return value2;
|
|
112
|
+
}
|
|
113
|
+
if (ch === "\\") {
|
|
114
|
+
next();
|
|
115
|
+
if (ch === "u") {
|
|
116
|
+
uffff = 0;
|
|
117
|
+
for (i = 0; i < 4; i++) {
|
|
118
|
+
hex = parseInt(next(), 16);
|
|
119
|
+
if (!isFinite(hex)) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
uffff = uffff * 16 + hex;
|
|
123
|
+
}
|
|
124
|
+
if (i < 4) {
|
|
125
|
+
error("Bad unicode escape");
|
|
126
|
+
}
|
|
127
|
+
value2 += String.fromCharCode(uffff);
|
|
128
|
+
} else if (typeof escapee[ch] === "string") {
|
|
129
|
+
value2 += escapee[ch];
|
|
130
|
+
} else {
|
|
131
|
+
error("Bad escaped character");
|
|
132
|
+
}
|
|
133
|
+
} else if (ch < " ") {
|
|
134
|
+
error("Bad control character");
|
|
135
|
+
} else {
|
|
136
|
+
value2 += ch;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
error(`Expected '"' instead of ${JSON.stringify(ch)}`);
|
|
141
|
+
}
|
|
142
|
+
return error("Unterminated string");
|
|
143
|
+
};
|
|
144
|
+
const white = () => {
|
|
145
|
+
while (ch && spaces.has(ch)) {
|
|
146
|
+
next();
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const word = () => {
|
|
150
|
+
switch (ch) {
|
|
151
|
+
case "t":
|
|
152
|
+
next();
|
|
153
|
+
next("r");
|
|
154
|
+
next("u");
|
|
155
|
+
next("e");
|
|
156
|
+
return;
|
|
157
|
+
case "f":
|
|
158
|
+
next();
|
|
159
|
+
next("a");
|
|
160
|
+
next("l");
|
|
161
|
+
next("s");
|
|
162
|
+
next("e");
|
|
163
|
+
return;
|
|
164
|
+
case "n":
|
|
165
|
+
next();
|
|
166
|
+
next("u");
|
|
167
|
+
next("l");
|
|
168
|
+
next("l");
|
|
169
|
+
return;
|
|
170
|
+
default:
|
|
171
|
+
error(`Unexpected ${JSON.stringify(ch)}`);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const array = () => {
|
|
175
|
+
next("[");
|
|
176
|
+
white();
|
|
177
|
+
if (ch === "]") {
|
|
178
|
+
next();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
while (ch) {
|
|
182
|
+
if (ch === "]") {
|
|
183
|
+
error("Trailing comma in array");
|
|
184
|
+
}
|
|
185
|
+
value();
|
|
186
|
+
white();
|
|
187
|
+
if (ch === "]") {
|
|
188
|
+
next();
|
|
189
|
+
return;
|
|
190
|
+
} else if (ch === ",") {
|
|
191
|
+
next();
|
|
192
|
+
white();
|
|
193
|
+
} else {
|
|
194
|
+
error(`Expected "," or "]" instead of ${stringify(ch)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
error("Unterminated array");
|
|
198
|
+
};
|
|
199
|
+
const object = () => {
|
|
200
|
+
let key;
|
|
201
|
+
const keys = /* @__PURE__ */ new Set();
|
|
202
|
+
next("{");
|
|
203
|
+
white();
|
|
204
|
+
if (ch === "}") {
|
|
205
|
+
next();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
while (ch) {
|
|
209
|
+
if (ch === "}") {
|
|
210
|
+
error("Trailing comma in object");
|
|
211
|
+
}
|
|
212
|
+
key = string();
|
|
213
|
+
white();
|
|
214
|
+
next(":");
|
|
215
|
+
if (keys.has(key)) {
|
|
216
|
+
warn(`Duplicate key ${stringify(key)}`);
|
|
217
|
+
} else {
|
|
218
|
+
keys.add(key);
|
|
219
|
+
}
|
|
220
|
+
value();
|
|
221
|
+
white();
|
|
222
|
+
if (ch === "}") {
|
|
223
|
+
next();
|
|
224
|
+
return;
|
|
225
|
+
} else if (ch === ",") {
|
|
226
|
+
next();
|
|
227
|
+
white();
|
|
228
|
+
} else {
|
|
229
|
+
error(`Expected "," or "}" instead of ${stringify(ch)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
error(`Expected '"'`);
|
|
233
|
+
};
|
|
234
|
+
const value = () => {
|
|
235
|
+
white();
|
|
236
|
+
switch (ch) {
|
|
237
|
+
case "{":
|
|
238
|
+
return object();
|
|
239
|
+
case "[":
|
|
240
|
+
return array();
|
|
241
|
+
case '"':
|
|
242
|
+
return string();
|
|
243
|
+
case "-":
|
|
244
|
+
return number();
|
|
245
|
+
default:
|
|
246
|
+
return ch >= "0" && ch <= "9" ? number() : word();
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
return (source) => {
|
|
250
|
+
text = source;
|
|
251
|
+
warnings = [];
|
|
252
|
+
at = 0;
|
|
253
|
+
ch = " ";
|
|
254
|
+
value();
|
|
255
|
+
white();
|
|
256
|
+
if (ch) {
|
|
257
|
+
error("Syntax error");
|
|
258
|
+
} else if (warnings.length > 0) {
|
|
259
|
+
throw { warnings };
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
})();
|
|
263
|
+
|
|
264
|
+
// src/index.ts
|
|
265
|
+
var wmf = "wiktionary|wiki(?:pedia|books|news|quote|source|versity|voyage)";
|
|
266
|
+
var rawurldecode = (str) => decodeURIComponent(str.replace(/%(?![\da-f]{2})/giu, "%25"));
|
|
267
|
+
var numToHex = (d) => Math.round(d * 255).toString(16).padStart(2, "0");
|
|
268
|
+
var regex = /* @__PURE__ */ (() => {
|
|
5
269
|
const hexColor = String.raw`#(?:[\da-f]{3,4}|(?:[\da-f]{2}){3,4})(?![\p{L}\p{N}_])`, rgbValue = String.raw`(?:\d*\.)?\d+%?`, hue = String.raw`(?:\d*\.)?\d+(?:deg|grad|rad|turn)?`, rgbColor = String.raw`rgba?\(\s*(?:${String.raw`${new Array(3).fill(rgbValue).join(String.raw`\s+`)}(?:\s*\/\s*${rgbValue})?`}|${String.raw`${new Array(3).fill(rgbValue).join(String.raw`\s*,\s*`)}(?:\s*,\s*${rgbValue})?`})\s*\)`, hslColor = String.raw`hsla?\(\s*(?:${String.raw`${hue}\s+${rgbValue}\s+${rgbValue}(?:\s*\/\s*${rgbValue})?`}|${String.raw`${hue}${String.raw`\s*,\s*(?:\d*\.)?\d+%`.repeat(2)}(?:\s*,\s*${rgbValue})?`})\s*\)`;
|
|
6
270
|
return {
|
|
7
271
|
full: new RegExp(String.raw`(^|[^\p{L}\p{N}_])(${hexColor}|${rgbColor}|${hslColor})`, "giu"),
|
|
8
272
|
rgb: new RegExp(String.raw`(^|[^\p{L}\p{N}_])(${hexColor}|${rgbColor})`, "giu")
|
|
9
273
|
};
|
|
10
274
|
})();
|
|
11
|
-
|
|
275
|
+
var splitColors = (str, hsl = true) => {
|
|
12
276
|
const pieces = [], re = regex[hsl ? "full" : "rgb"];
|
|
13
277
|
re.lastIndex = 0;
|
|
14
278
|
let mt = re.exec(str), lastIndex = 0;
|
|
@@ -26,7 +290,7 @@ const splitColors = (str, hsl = true) => {
|
|
|
26
290
|
}
|
|
27
291
|
return pieces;
|
|
28
292
|
};
|
|
29
|
-
|
|
293
|
+
var sanitizeInlineStyle = (style) => style.replace(/[{}]/gu, (p) => p === "{" ? "{" : "}");
|
|
30
294
|
function getRegex(f) {
|
|
31
295
|
const map = /* @__PURE__ */ new Map(), weakMap = /* @__PURE__ */ new WeakMap();
|
|
32
296
|
return (s) => {
|
|
@@ -41,28 +305,82 @@ function getRegex(f) {
|
|
|
41
305
|
return re;
|
|
42
306
|
};
|
|
43
307
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
308
|
+
var getObjRegex = getRegex;
|
|
309
|
+
var splitLines = (str) => {
|
|
310
|
+
const lines = [];
|
|
311
|
+
let start = 0;
|
|
312
|
+
for (const line of str.split("\n")) {
|
|
313
|
+
const end = start + line.length;
|
|
314
|
+
lines.push([line, start, end]);
|
|
315
|
+
start = end + 1;
|
|
316
|
+
}
|
|
317
|
+
return lines;
|
|
318
|
+
};
|
|
319
|
+
var mt2num = (mt) => mt && Number(mt[1]);
|
|
320
|
+
var formatJsonError = (str, errors) => {
|
|
321
|
+
let lines;
|
|
322
|
+
for (const error of errors) {
|
|
323
|
+
const { line, column, position } = error;
|
|
324
|
+
if (position === null || position === void 0) {
|
|
325
|
+
if (line) {
|
|
326
|
+
lines ??= splitLines(str);
|
|
327
|
+
error.column ??= 1;
|
|
328
|
+
error.position = lines[line - 1][1] + (error.column - 1);
|
|
329
|
+
} else {
|
|
330
|
+
error.position = 0;
|
|
331
|
+
error.line = 1;
|
|
332
|
+
error.column = 1;
|
|
333
|
+
}
|
|
334
|
+
} else if (!line || !column) {
|
|
335
|
+
lines ??= splitLines(str);
|
|
336
|
+
error.line = lines.findIndex(([, , end]) => position <= end) + 1;
|
|
337
|
+
error.column = position - lines[error.line - 1][1] + 1;
|
|
50
338
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
339
|
+
}
|
|
340
|
+
return errors;
|
|
341
|
+
};
|
|
342
|
+
var lintJSONNative = (str, force) => {
|
|
343
|
+
if (force || str.trim()) {
|
|
344
|
+
try {
|
|
345
|
+
JSON.parse(str);
|
|
346
|
+
} catch (e) {
|
|
53
347
|
const { message } = e, line = mt2num(/\bline (\d+)/u.exec(message)), column = mt2num(/\bcolumn (\d+)/u.exec(message)), position = mt2num(/\bposition (\d+)/u.exec(message));
|
|
54
|
-
return [{ message, line, column, position }];
|
|
348
|
+
return formatJsonError(str, [{ message, line, column, position, severity: "error" }]);
|
|
55
349
|
}
|
|
56
350
|
}
|
|
57
351
|
return [];
|
|
58
352
|
};
|
|
353
|
+
var lintJSON = (str) => {
|
|
354
|
+
if (!str.trim()) {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
let errors;
|
|
358
|
+
try {
|
|
359
|
+
json_parse(str);
|
|
360
|
+
} catch (e) {
|
|
361
|
+
if (!(e instanceof Error)) {
|
|
362
|
+
const { warnings, ...error } = e;
|
|
363
|
+
if (error.message) {
|
|
364
|
+
warnings.push(error);
|
|
365
|
+
}
|
|
366
|
+
errors = formatJsonError(str, warnings);
|
|
367
|
+
if (error.message) {
|
|
368
|
+
return errors;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const nativeErrors = lintJSONNative(str);
|
|
373
|
+
return errors ? [...nativeErrors, ...errors] : nativeErrors;
|
|
374
|
+
};
|
|
59
375
|
export {
|
|
60
376
|
getObjRegex,
|
|
61
377
|
getRegex,
|
|
62
378
|
lintJSON,
|
|
379
|
+
lintJSONNative,
|
|
63
380
|
numToHex,
|
|
64
381
|
rawurldecode,
|
|
65
382
|
sanitizeInlineStyle,
|
|
66
383
|
splitColors,
|
|
384
|
+
splitLines,
|
|
67
385
|
wmf
|
|
68
386
|
};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
json_parse.js
|
|
4
|
+
2016-05-02
|
|
5
|
+
|
|
6
|
+
Public Domain.
|
|
7
|
+
|
|
8
|
+
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
|
9
|
+
|
|
10
|
+
This file creates a json_parse function.
|
|
11
|
+
|
|
12
|
+
json_parse(text)
|
|
13
|
+
This method parses a JSON text and throws a SyntaxError exception.
|
|
14
|
+
|
|
15
|
+
This is a reference implementation. You are free to copy, modify, or redistribute.
|
|
16
|
+
|
|
17
|
+
Original source:
|
|
18
|
+
https://github.com/douglascrockford/JSON-js/blob/107fc93c94aa3a9c7b48548631593ecf3aac60d2/json_parse.js
|
|
19
|
+
|
|
20
|
+
Modifications:
|
|
21
|
+
- Only returns errors and warnings instead of the parsed value.
|
|
22
|
+
- Better agreement with JSON specification.
|
|
23
|
+
- Warnings for duplicate object keys and unsafe integers.
|
|
24
|
+
|
|
25
|
+
This code should be minified before deployment.
|
|
26
|
+
See http://javascript.crockford.com/jsmin.html
|
|
27
|
+
|
|
28
|
+
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL.
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.json_parse = void 0;
|
|
32
|
+
exports.json_parse = (() => {
|
|
33
|
+
// This is a function that can parse a JSON text.
|
|
34
|
+
// It is a simple, recursive descent parser.
|
|
35
|
+
// It does not use eval or regular expressions,
|
|
36
|
+
// so it can be used as a model for implementing a JSON parser in other languages.
|
|
37
|
+
// We are defining the function inside of another function to avoid creating global variables.
|
|
38
|
+
let at; // The index of the current character
|
|
39
|
+
let ch; // The current character
|
|
40
|
+
const escapee = {
|
|
41
|
+
'"': '"',
|
|
42
|
+
"\\": "\\",
|
|
43
|
+
"/": "/",
|
|
44
|
+
b: "\b",
|
|
45
|
+
f: "\f",
|
|
46
|
+
n: "\n",
|
|
47
|
+
r: "\r",
|
|
48
|
+
t: "\t",
|
|
49
|
+
};
|
|
50
|
+
const spaces = new Set([" ", "\t", "\n", "\r"]);
|
|
51
|
+
let text;
|
|
52
|
+
let warnings;
|
|
53
|
+
const stringify = (c) => {
|
|
54
|
+
if (c === "") {
|
|
55
|
+
return "end of input";
|
|
56
|
+
}
|
|
57
|
+
return c === '"' ? `'"'` : JSON.stringify(c);
|
|
58
|
+
};
|
|
59
|
+
const warn = (m) => {
|
|
60
|
+
// Log warning when something is wrong.
|
|
61
|
+
warnings.push({
|
|
62
|
+
message: m,
|
|
63
|
+
position: at - 1,
|
|
64
|
+
severity: "warning",
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const error = (m) => {
|
|
68
|
+
// Call error when something is wrong.
|
|
69
|
+
throw {
|
|
70
|
+
warnings,
|
|
71
|
+
message: m,
|
|
72
|
+
position: at - 1,
|
|
73
|
+
severity: "error",
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const next = (c) => {
|
|
77
|
+
// If a c parameter is provided, verify that it matches the current character.
|
|
78
|
+
if (c && c !== ch) {
|
|
79
|
+
error(`Expected ${stringify(c)} instead of ${stringify(ch)}`);
|
|
80
|
+
}
|
|
81
|
+
// Get the next character. When there are no more characters, return the empty string.
|
|
82
|
+
ch = text.charAt(at);
|
|
83
|
+
at += 1;
|
|
84
|
+
return ch;
|
|
85
|
+
};
|
|
86
|
+
const number = () => {
|
|
87
|
+
// Parse a number value.
|
|
88
|
+
let value;
|
|
89
|
+
let string = "";
|
|
90
|
+
if (ch === "-") {
|
|
91
|
+
string = "-";
|
|
92
|
+
next();
|
|
93
|
+
}
|
|
94
|
+
if (ch === "0") {
|
|
95
|
+
string += ch;
|
|
96
|
+
next();
|
|
97
|
+
if (ch >= "0" && ch <= "9") {
|
|
98
|
+
error("Bad number");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (ch >= "1" && ch <= "9") {
|
|
102
|
+
while (ch >= "0" && ch <= "9") {
|
|
103
|
+
string += ch;
|
|
104
|
+
next();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
error("No number after minus sign");
|
|
109
|
+
}
|
|
110
|
+
if (ch !== "." && ch !== "e" && ch !== "E") {
|
|
111
|
+
value = Number(string);
|
|
112
|
+
if (!Number.isSafeInteger(value)) {
|
|
113
|
+
warn("Not a safe integer");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (ch === ".") {
|
|
117
|
+
string += ".";
|
|
118
|
+
next();
|
|
119
|
+
if (ch < "0" || ch > "9") {
|
|
120
|
+
error("Unterminated fractional number");
|
|
121
|
+
}
|
|
122
|
+
while (ch >= "0" && ch <= "9") {
|
|
123
|
+
string += ch;
|
|
124
|
+
next();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (ch === "e" || ch === "E") {
|
|
128
|
+
string += ch;
|
|
129
|
+
next();
|
|
130
|
+
// @ts-expect-error `ch` modified
|
|
131
|
+
if (ch === "-" || ch === "+") {
|
|
132
|
+
string += ch;
|
|
133
|
+
next();
|
|
134
|
+
}
|
|
135
|
+
while (ch >= "0" && ch <= "9") {
|
|
136
|
+
string += ch;
|
|
137
|
+
next();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
value ??= Number(string);
|
|
141
|
+
if (!Number.isFinite(value)) {
|
|
142
|
+
error("Bad number");
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const string = () => {
|
|
146
|
+
// Parse a string value.
|
|
147
|
+
let hex;
|
|
148
|
+
let i;
|
|
149
|
+
let value = "";
|
|
150
|
+
let uffff;
|
|
151
|
+
// When parsing for string values, we must look for " and \ characters.
|
|
152
|
+
if (ch === '"') {
|
|
153
|
+
while (next()) {
|
|
154
|
+
if (ch === '"') {
|
|
155
|
+
next();
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
if (ch === "\\") {
|
|
159
|
+
next();
|
|
160
|
+
if (ch === "u") {
|
|
161
|
+
uffff = 0;
|
|
162
|
+
for (i = 0; i < 4; i++) {
|
|
163
|
+
hex = parseInt(next(), 16);
|
|
164
|
+
if (!isFinite(hex)) {
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
uffff = uffff * 16 + hex;
|
|
168
|
+
}
|
|
169
|
+
if (i < 4) {
|
|
170
|
+
error("Bad unicode escape");
|
|
171
|
+
}
|
|
172
|
+
value += String.fromCharCode(uffff);
|
|
173
|
+
}
|
|
174
|
+
else if (typeof escapee[ch] === "string") {
|
|
175
|
+
value += escapee[ch];
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
error("Bad escaped character");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (ch < " ") {
|
|
182
|
+
error("Bad control character");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
value += ch;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
error(`Expected '"' instead of ${JSON.stringify(ch)}`);
|
|
191
|
+
}
|
|
192
|
+
return error("Unterminated string");
|
|
193
|
+
};
|
|
194
|
+
const white = () => {
|
|
195
|
+
// Skip whitespace.
|
|
196
|
+
while (ch && spaces.has(ch)) {
|
|
197
|
+
next();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const word = () => {
|
|
201
|
+
// true, false, or null.
|
|
202
|
+
switch (ch) {
|
|
203
|
+
case "t":
|
|
204
|
+
next();
|
|
205
|
+
next("r");
|
|
206
|
+
next("u");
|
|
207
|
+
next("e");
|
|
208
|
+
return;
|
|
209
|
+
case "f":
|
|
210
|
+
next();
|
|
211
|
+
next("a");
|
|
212
|
+
next("l");
|
|
213
|
+
next("s");
|
|
214
|
+
next("e");
|
|
215
|
+
return;
|
|
216
|
+
case "n":
|
|
217
|
+
next();
|
|
218
|
+
next("u");
|
|
219
|
+
next("l");
|
|
220
|
+
next("l");
|
|
221
|
+
return;
|
|
222
|
+
default:
|
|
223
|
+
error(`Unexpected ${JSON.stringify(ch)}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const array = () => {
|
|
227
|
+
// Parse an array value.
|
|
228
|
+
next("[");
|
|
229
|
+
white();
|
|
230
|
+
if (ch === "]") {
|
|
231
|
+
next();
|
|
232
|
+
return; // empty array
|
|
233
|
+
}
|
|
234
|
+
while (ch) {
|
|
235
|
+
if (ch === "]") {
|
|
236
|
+
error("Trailing comma in array");
|
|
237
|
+
}
|
|
238
|
+
value();
|
|
239
|
+
white();
|
|
240
|
+
if (ch === "]") {
|
|
241
|
+
next();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
else if (ch === ",") {
|
|
245
|
+
next();
|
|
246
|
+
white();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
error(`Expected "," or "]" instead of ${stringify(ch)}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
error("Unterminated array");
|
|
253
|
+
};
|
|
254
|
+
const object = () => {
|
|
255
|
+
// Parse an object value.
|
|
256
|
+
let key;
|
|
257
|
+
const keys = new Set();
|
|
258
|
+
next("{");
|
|
259
|
+
white();
|
|
260
|
+
if (ch === "}") {
|
|
261
|
+
next();
|
|
262
|
+
return; // empty object
|
|
263
|
+
}
|
|
264
|
+
while (ch) {
|
|
265
|
+
if (ch === "}") {
|
|
266
|
+
error("Trailing comma in object");
|
|
267
|
+
}
|
|
268
|
+
key = string();
|
|
269
|
+
white();
|
|
270
|
+
next(":");
|
|
271
|
+
if (keys.has(key)) {
|
|
272
|
+
warn(`Duplicate key ${stringify(key)}`);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
keys.add(key);
|
|
276
|
+
}
|
|
277
|
+
value();
|
|
278
|
+
white();
|
|
279
|
+
if (ch === "}") {
|
|
280
|
+
next();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
else if (ch === ",") {
|
|
284
|
+
next();
|
|
285
|
+
white();
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
error(`Expected "," or "}" instead of ${stringify(ch)}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
error(`Expected '"'`);
|
|
292
|
+
};
|
|
293
|
+
const value = () => {
|
|
294
|
+
// Parse a JSON value. It could be an object, an array, a string, a number,
|
|
295
|
+
// or a word.
|
|
296
|
+
white();
|
|
297
|
+
switch (ch) {
|
|
298
|
+
case "{":
|
|
299
|
+
return object();
|
|
300
|
+
case "[":
|
|
301
|
+
return array();
|
|
302
|
+
case '"':
|
|
303
|
+
return string();
|
|
304
|
+
case "-":
|
|
305
|
+
return number();
|
|
306
|
+
default:
|
|
307
|
+
return ch >= "0" && ch <= "9"
|
|
308
|
+
? number()
|
|
309
|
+
: word();
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
// Return the json_parse function. It will have access to all of the above
|
|
313
|
+
// functions and variables.
|
|
314
|
+
return (source) => {
|
|
315
|
+
text = source;
|
|
316
|
+
warnings = [];
|
|
317
|
+
at = 0;
|
|
318
|
+
ch = " ";
|
|
319
|
+
value();
|
|
320
|
+
white();
|
|
321
|
+
if (ch) {
|
|
322
|
+
error("Syntax error");
|
|
323
|
+
}
|
|
324
|
+
else if (warnings.length > 0) {
|
|
325
|
+
throw { warnings };
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bhsd/common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"homepage": "https://github.com/bhsd-harry/common#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/bhsd-harry/common/issues"
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"author": "Bhsd",
|
|
10
10
|
"files": [
|
|
11
|
-
"/dist
|
|
11
|
+
"/dist/*.js",
|
|
12
|
+
"/dist/index.mjs",
|
|
13
|
+
"/dist/index.d.ts"
|
|
12
14
|
],
|
|
13
15
|
"exports": {
|
|
14
16
|
".": {
|
|
@@ -21,13 +23,15 @@
|
|
|
21
23
|
"sideEffects": false,
|
|
22
24
|
"scripts": {
|
|
23
25
|
"prepublishOnly": "npm run build",
|
|
24
|
-
"build": "tsc && esbuild src/index.ts --charset=utf8 --target=es2024 --format=esm --outfile=dist/index.mjs",
|
|
26
|
+
"build": "tsc && esbuild src/index.ts --charset=utf8 --target=es2024 --bundle --format=esm --outfile=dist/index.mjs && cp dist/index.d.ts dist/index.d.mts",
|
|
25
27
|
"lint:ts": "tsc --noEmit && eslint --cache .",
|
|
26
|
-
"lint": "npm run lint:ts"
|
|
28
|
+
"lint": "npm run lint:ts",
|
|
29
|
+
"test": "mocha"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
32
|
"@bhsd/code-standard": "^1.3.2",
|
|
30
33
|
"@stylistic/eslint-plugin": "^5.5.0",
|
|
34
|
+
"@types/mocha": "^10.0.10",
|
|
31
35
|
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
|
32
36
|
"@typescript-eslint/parser": "^8.46.4",
|
|
33
37
|
"esbuild": "^0.25.12",
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
"eslint-plugin-promise": "^7.2.1",
|
|
40
44
|
"eslint-plugin-regexp": "^2.10.0",
|
|
41
45
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
46
|
+
"mocha": "^11.7.5",
|
|
42
47
|
"typescript": "^5.9.3"
|
|
43
48
|
},
|
|
44
49
|
"engines": {
|