slimjson 1.0.4 → 1.1.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/.claude/settings.local.json +7 -2
- package/.idea/git_toolbox_prj.xml +15 -0
- package/.idea/modules.xml +8 -0
- package/.idea/slimjson.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +519 -361
- package/README_EN.md +508 -350
- package/compress-file.js +41 -41
- package/compress-ratio.js +70 -70
- package/compress-test.js +436 -436
- package/compress.js +268 -146
- package/decompress-file.js +42 -42
- package/esm.mjs +5 -4
- package/package.json +24 -24
- package/test.js +975 -975
- package/data/searchGroup.json +0 -96365
package/compress.js
CHANGED
|
@@ -7,181 +7,302 @@
|
|
|
7
7
|
* - 缺失的 key 在 rows 中填充 null
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
/**
|
|
11
|
+
* 合并两个 schema(取键的并集,保持顺序)
|
|
12
|
+
* 对象 schema 合并键,嵌套数组 schema 递归合并内层,原始值数组取第一个
|
|
13
|
+
*/
|
|
14
|
+
function mergeSchemas(s1, s2) {
|
|
15
|
+
if (!Array.isArray(s1) || !Array.isArray(s2)) return s1;
|
|
16
|
+
|
|
17
|
+
const first1 = s1[0];
|
|
18
|
+
const first2 = s2[0];
|
|
19
|
+
|
|
20
|
+
// 两者都是对象 schema(元素是字符串或 {key: sub} 对象)→ 合并字段
|
|
21
|
+
const isObj1 = s1.length === 0 || typeof first1 === 'string' ||
|
|
22
|
+
(typeof first1 === 'object' && first1 !== null && !Array.isArray(first1));
|
|
23
|
+
const isObj2 = s2.length === 0 || typeof first2 === 'string' ||
|
|
24
|
+
(typeof first2 === 'object' && first2 !== null && !Array.isArray(first2));
|
|
25
|
+
|
|
26
|
+
if (isObj1 && isObj2) {
|
|
27
|
+
const merged = [...s1];
|
|
28
|
+
const existingKeys = new Set();
|
|
29
|
+
for (const field of merged) {
|
|
30
|
+
existingKeys.add(typeof field === 'string' ? field : Object.keys(field)[0]);
|
|
31
|
+
}
|
|
32
|
+
for (const field of s2) {
|
|
33
|
+
const key = typeof field === 'string' ? field : Object.keys(field)[0];
|
|
34
|
+
if (!existingKeys.has(key)) {
|
|
35
|
+
merged.push(field);
|
|
36
|
+
existingKeys.add(key);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return merged;
|
|
18
40
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
|
|
42
|
+
// 两者都是数组(不是对象 schema)→ 递归合并第一个元素
|
|
43
|
+
if (Array.isArray(first1) && Array.isArray(first2)) {
|
|
44
|
+
return [mergeSchemas(first1, first2)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 其他情况(原始值数组或类型不匹配)→ 取第一个
|
|
48
|
+
return s1;
|
|
23
49
|
}
|
|
24
50
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!seen.has(k)) {
|
|
36
|
-
seen.add(k);
|
|
37
|
-
orderedKeys.push(k);
|
|
51
|
+
/**
|
|
52
|
+
* 推断值的 schema(从所有数据中收集完整结构)
|
|
53
|
+
*/
|
|
54
|
+
function inferSchema(value) {
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
if (value.length === 0) return [[]];
|
|
57
|
+
const first = value[0];
|
|
58
|
+
if (typeof first === 'object' && first !== null && !Array.isArray(first)) {
|
|
59
|
+
// 对象数组
|
|
60
|
+
return [inferObjectSchema(value)];
|
|
38
61
|
}
|
|
39
|
-
|
|
62
|
+
if (Array.isArray(first)) {
|
|
63
|
+
// 数组的数组:对每个内层数组递归推断 schema,然后合并
|
|
64
|
+
let merged = null;
|
|
65
|
+
for (const inner of value) {
|
|
66
|
+
const s = inferSchema(inner);
|
|
67
|
+
if (s) {
|
|
68
|
+
merged = merged ? mergeSchemas(merged, s) : s;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return [merged || inferSchema(first)];
|
|
72
|
+
}
|
|
73
|
+
// 检查是否含对象(混合数组)
|
|
74
|
+
const objects = value.filter(v => v && typeof v === 'object' && !Array.isArray(v));
|
|
75
|
+
if (objects.length > 0) {
|
|
76
|
+
return [inferObjectSchema(objects)];
|
|
77
|
+
}
|
|
78
|
+
// 原始值数组 - 不压缩,由父级处理
|
|
79
|
+
return undefined;
|
|
40
80
|
}
|
|
41
|
-
|
|
81
|
+
if (typeof value === 'object' && value !== null) {
|
|
82
|
+
return inferObjectSchema([value]);
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
42
86
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
87
|
+
/**
|
|
88
|
+
* 从多个对象中推断对象 schema(取所有 key 的并集)
|
|
89
|
+
*/
|
|
90
|
+
function inferObjectSchema(objects) {
|
|
91
|
+
const keyOrder = [];
|
|
92
|
+
const keyValues = new Map();
|
|
93
|
+
|
|
94
|
+
for (const obj of objects) {
|
|
95
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) continue;
|
|
96
|
+
for (const key of Object.keys(obj)) {
|
|
97
|
+
if (!keyValues.has(key)) {
|
|
98
|
+
keyOrder.push(key);
|
|
99
|
+
keyValues.set(key, []);
|
|
100
|
+
}
|
|
101
|
+
const val = obj[key];
|
|
102
|
+
if (val != null) {
|
|
103
|
+
keyValues.get(key).push(val);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
52
106
|
}
|
|
53
107
|
|
|
54
|
-
|
|
108
|
+
return keyOrder.map(key => {
|
|
109
|
+
const values = keyValues.get(key) || [];
|
|
110
|
+
if (values.length === 0) return key;
|
|
55
111
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (v !== undefined && v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
63
|
-
allNestedObjs.push(v);
|
|
64
|
-
}
|
|
112
|
+
const sample = values[0];
|
|
113
|
+
|
|
114
|
+
// 值是对象 → 递归推断对象 schema(单对象,非数组)
|
|
115
|
+
if (typeof sample === 'object' && sample !== null && !Array.isArray(sample)) {
|
|
116
|
+
const subObjects = values.filter(v => typeof v === 'object' && v !== null && !Array.isArray(v));
|
|
117
|
+
return { [key]: inferObjectSchema(subObjects) };
|
|
65
118
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
119
|
+
|
|
120
|
+
// 值是数组
|
|
121
|
+
if (Array.isArray(sample)) {
|
|
122
|
+
// 空数组 → 无法推断,用 key 名
|
|
123
|
+
if (sample.length === 0) return key;
|
|
124
|
+
|
|
125
|
+
// 数组元素是对象 → 对象数组:{ key: [objectSchema] }
|
|
126
|
+
if (typeof sample[0] === 'object' && sample[0] !== null && !Array.isArray(sample[0])) {
|
|
127
|
+
const allItems = [];
|
|
128
|
+
for (const v of values) {
|
|
129
|
+
if (Array.isArray(v)) {
|
|
130
|
+
for (const item of v) {
|
|
131
|
+
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
132
|
+
allItems.push(item);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { [key]: [inferObjectSchema(allItems)] };
|
|
80
138
|
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
keys.push({ [keyName]: buildKeys(allItems) });
|
|
85
139
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
140
|
+
// 数组元素是数组 → 检查是否含对象
|
|
141
|
+
if (Array.isArray(sample[0])) {
|
|
142
|
+
// 不含对象的嵌套数组(如 [[1,2],[3,4]])→ 不压缩,直接用 key 名
|
|
143
|
+
if (!containsObject(sample)) return key;
|
|
144
|
+
|
|
145
|
+
// 含对象的嵌套数组 → 递归推断内层 schema 并合并
|
|
146
|
+
let merged = null;
|
|
147
|
+
for (const v of values) {
|
|
148
|
+
if (Array.isArray(v)) {
|
|
149
|
+
const s = inferSchema(v);
|
|
150
|
+
if (s) {
|
|
151
|
+
// inferSchema 返回 [innerSchema],取 innerSchema 用于合并
|
|
152
|
+
const inner = Array.isArray(s) && s.length === 1 ? s[0] : s;
|
|
153
|
+
merged = merged ? mergeSchemas(merged, inner) : inner;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// 再包一层 [] 表示"数组的数组"
|
|
158
|
+
return { [key]: [merged || inferSchema(sample[0])] };
|
|
159
|
+
}
|
|
91
160
|
|
|
92
|
-
|
|
93
|
-
|
|
161
|
+
// 原始值数组(如 ["张三","李四"])→ 不压缩,直接用 key 名
|
|
162
|
+
return key;
|
|
163
|
+
}
|
|
94
164
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
function trimTrailingNulls(arr) {
|
|
99
|
-
let end = arr.length;
|
|
100
|
-
while (end > 0 && arr[end - 1] === null) end--;
|
|
101
|
-
if (end === arr.length) return arr;
|
|
102
|
-
return arr.slice(0, end);
|
|
165
|
+
// 原始值 → 直接用 key 名
|
|
166
|
+
return key;
|
|
167
|
+
});
|
|
103
168
|
}
|
|
104
169
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (val === undefined || val === null) {
|
|
118
|
-
row.push(null); // 字段缺失或为 null
|
|
119
|
-
|
|
120
|
-
} else if (Array.isArray(val)) {
|
|
121
|
-
// 对象数组
|
|
122
|
-
const arr = val.map(item => buildRow(item, childKeys, trim));
|
|
123
|
-
row.push(trim ? arr.map(r => trimTrailingNulls(r)) : arr);
|
|
170
|
+
/**
|
|
171
|
+
* 使用已知 schema 压缩值为 data
|
|
172
|
+
*/
|
|
173
|
+
function compressWithSchema(value, schema) {
|
|
174
|
+
if (schema === undefined) return value;
|
|
175
|
+
|
|
176
|
+
// schema 是 [innerSchema] → 值是数组
|
|
177
|
+
if (Array.isArray(schema) && schema.length === 1 && Array.isArray(schema[0])) {
|
|
178
|
+
const inner = schema[0];
|
|
179
|
+
if (!Array.isArray(value)) return null;
|
|
180
|
+
return value.map(item => compressWithSchema(item, inner));
|
|
181
|
+
}
|
|
124
182
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
row.push(trim ? trimTrailingNulls(sub) : sub);
|
|
129
|
-
}
|
|
183
|
+
// schema 包含 undefined → 原始值数组,不压缩
|
|
184
|
+
if (Array.isArray(schema) && schema.some(s => s === undefined || s === null)) {
|
|
185
|
+
return value;
|
|
130
186
|
}
|
|
131
|
-
}
|
|
132
|
-
if (trim) {
|
|
133
|
-
return trimTrailingNulls(row);
|
|
134
|
-
}
|
|
135
|
-
return row;
|
|
136
|
-
}
|
|
137
187
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
188
|
+
// schema 是数组(对象 schema)→ 值是对象
|
|
189
|
+
if (Array.isArray(schema)) {
|
|
190
|
+
if (Array.isArray(value)) return value.length === 0 ? [] : null;
|
|
191
|
+
if (!value || typeof value !== 'object') return value;
|
|
192
|
+
return schema.map(fieldDef => {
|
|
193
|
+
let key, valueSchema;
|
|
194
|
+
if (typeof fieldDef === 'string') {
|
|
195
|
+
key = fieldDef;
|
|
196
|
+
valueSchema = undefined;
|
|
197
|
+
} else {
|
|
198
|
+
key = Object.keys(fieldDef)[0];
|
|
199
|
+
valueSchema = fieldDef[key];
|
|
200
|
+
}
|
|
201
|
+
const val = value[key];
|
|
202
|
+
if (val == null) return null;
|
|
203
|
+
return compressWithSchema(val, valueSchema);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
152
206
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const obj = {};
|
|
156
|
-
for (let i = 0; i < keys.length; i++) {
|
|
157
|
-
const key = keys[i];
|
|
158
|
-
const val = row[i];
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
159
209
|
|
|
160
|
-
|
|
161
|
-
|
|
210
|
+
/**
|
|
211
|
+
* 判断值是否包含对象(递归检查)
|
|
212
|
+
*/
|
|
213
|
+
function containsObject(value) {
|
|
214
|
+
if (value === null || typeof value !== 'object') return false;
|
|
215
|
+
if (!Array.isArray(value)) return true;
|
|
216
|
+
for (const item of value) {
|
|
217
|
+
if (containsObject(item)) return true;
|
|
218
|
+
}
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
162
221
|
|
|
163
|
-
|
|
164
|
-
|
|
222
|
+
/**
|
|
223
|
+
* 递归去掉数组尾部连续 null
|
|
224
|
+
*/
|
|
225
|
+
function trimTrailingNullsDeep(data) {
|
|
226
|
+
if (!Array.isArray(data)) return data;
|
|
227
|
+
const trimmed = data.map(item => trimTrailingNullsDeep(item));
|
|
228
|
+
while (trimmed.length > 0 && trimmed[trimmed.length - 1] === null) {
|
|
229
|
+
trimmed.pop();
|
|
230
|
+
}
|
|
231
|
+
return trimmed;
|
|
232
|
+
}
|
|
165
233
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
234
|
+
/**
|
|
235
|
+
* 压缩任意 JSON 值为 { schema, data } 格式
|
|
236
|
+
* 不含对象的数组和非对象非数组的值直接返回
|
|
237
|
+
* @param {any} value - 要压缩的值
|
|
238
|
+
* @param {object} [options] - 选项
|
|
239
|
+
* @param {boolean} [options.trimTrailingNulls=false] - 去掉数组尾部连续 null
|
|
240
|
+
*/
|
|
241
|
+
function compress(value, options) {
|
|
242
|
+
if (!containsObject(value)) return value;
|
|
243
|
+
const schema = inferSchema(value);
|
|
244
|
+
let data = compressWithSchema(value, schema);
|
|
245
|
+
if (options && options.trimTrailingNulls) {
|
|
246
|
+
data = trimTrailingNullsDeep(data);
|
|
247
|
+
}
|
|
248
|
+
return { schema, data };
|
|
249
|
+
}
|
|
169
250
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
251
|
+
/**
|
|
252
|
+
* 使用 schema 还原 data 为原始对象
|
|
253
|
+
*/
|
|
254
|
+
function decompressWithSchema(data, schema) {
|
|
255
|
+
if (schema === undefined) return data;
|
|
256
|
+
if (data == null) return null;
|
|
257
|
+
|
|
258
|
+
// schema 是 [innerSchema] → 还原为数组
|
|
259
|
+
if (Array.isArray(schema) && schema.length === 1 && Array.isArray(schema[0])) {
|
|
260
|
+
if (!Array.isArray(data)) return data;
|
|
261
|
+
const inner = schema[0];
|
|
262
|
+
return data.map(item => decompressWithSchema(item, inner));
|
|
263
|
+
}
|
|
173
264
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
265
|
+
// schema 是数组
|
|
266
|
+
if (Array.isArray(schema)) {
|
|
267
|
+
// schema 包含 undefined 元素 → 原始值数组,不压缩
|
|
268
|
+
if (schema.some(s => s === undefined || s === null)) return data;
|
|
269
|
+
|
|
270
|
+
// 原始值(混合数组中的原始元素)→ 直接返回
|
|
271
|
+
if (typeof data !== 'object' || data === null) return data;
|
|
272
|
+
|
|
273
|
+
// 对象 schema → 还原为对象
|
|
274
|
+
const obj = {};
|
|
275
|
+
for (let i = 0; i < schema.length; i++) {
|
|
276
|
+
const fieldDef = schema[i];
|
|
277
|
+
let key, valueSchema;
|
|
278
|
+
if (typeof fieldDef === 'string') {
|
|
279
|
+
key = fieldDef;
|
|
280
|
+
valueSchema = undefined;
|
|
281
|
+
} else if (typeof fieldDef === 'object' && fieldDef !== null) {
|
|
282
|
+
key = Object.keys(fieldDef)[0];
|
|
283
|
+
valueSchema = fieldDef[key];
|
|
284
|
+
} else {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const val = data[i];
|
|
288
|
+
if (val === undefined) { obj[key] = null; continue; }
|
|
289
|
+
obj[key] = decompressWithSchema(val, valueSchema);
|
|
177
290
|
}
|
|
178
|
-
|
|
291
|
+
return obj;
|
|
179
292
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return compressed.rows.map(row => buildFromRow(row, compressed.keys));
|
|
293
|
+
|
|
294
|
+
return data;
|
|
183
295
|
}
|
|
184
296
|
|
|
297
|
+
/**
|
|
298
|
+
* 从 { schema, data } 还原为原始值
|
|
299
|
+
* 如果输入不含 schema(直接值),原样返回
|
|
300
|
+
*/
|
|
301
|
+
function decompress(compressed) {
|
|
302
|
+
if (compressed === null || typeof compressed !== 'object' || Array.isArray(compressed)) return compressed;
|
|
303
|
+
if (!('data' in compressed)) return compressed;
|
|
304
|
+
return decompressWithSchema(compressed.data, compressed.schema);
|
|
305
|
+
}
|
|
185
306
|
/* ============================================================
|
|
186
307
|
判断字符串是否可安全省略引号
|
|
187
308
|
============================================================ */
|
|
@@ -421,3 +542,4 @@ function parse(text) {
|
|
|
421
542
|
}
|
|
422
543
|
|
|
423
544
|
module.exports = { compress, decompress, stringify, parse };
|
|
545
|
+
module.exports.default = module.exports;
|
package/decompress-file.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 用法: node decompress-file.js <压缩文件> [输出.json]
|
|
3
|
-
*
|
|
4
|
-
* 默认输出文件名: <输入名>.json(去掉 .slim 后缀)
|
|
5
|
-
*/
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const { decompress, parse } = require('./compress');
|
|
9
|
-
|
|
10
|
-
const input = process.argv[2];
|
|
11
|
-
if (!input) {
|
|
12
|
-
console.error('用法: node decompress-file.js <压缩文件> [输出.json]');
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
if (!fs.existsSync(input)) {
|
|
16
|
-
console.error(`文件不存在: ${input}`);
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const output = process.argv[3] || input.replace(/\.json\.slim$/i, '.json');
|
|
21
|
-
|
|
22
|
-
let text;
|
|
23
|
-
try {
|
|
24
|
-
text = fs.readFileSync(input, 'utf8');
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.error(`读取文件失败: ${e.message}`);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let compressed;
|
|
31
|
-
try {
|
|
32
|
-
compressed = parse(text);
|
|
33
|
-
} catch (e) {
|
|
34
|
-
console.error(`解析失败: ${e.message}`);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const data = decompress(compressed);
|
|
39
|
-
fs.writeFileSync(output, JSON.stringify(data, null, 2), 'utf8');
|
|
40
|
-
|
|
41
|
-
console.log(`输入: ${path.basename(input)}`);
|
|
42
|
-
console.log(`输出: ${path.basename(output)} (${data.length} 条)`);
|
|
1
|
+
/**
|
|
2
|
+
* 用法: node decompress-file.js <压缩文件> [输出.json]
|
|
3
|
+
*
|
|
4
|
+
* 默认输出文件名: <输入名>.json(去掉 .slim 后缀)
|
|
5
|
+
*/
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { decompress, parse } = require('./compress');
|
|
9
|
+
|
|
10
|
+
const input = process.argv[2];
|
|
11
|
+
if (!input) {
|
|
12
|
+
console.error('用法: node decompress-file.js <压缩文件> [输出.json]');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
if (!fs.existsSync(input)) {
|
|
16
|
+
console.error(`文件不存在: ${input}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const output = process.argv[3] || input.replace(/\.json\.slim$/i, '.json');
|
|
21
|
+
|
|
22
|
+
let text;
|
|
23
|
+
try {
|
|
24
|
+
text = fs.readFileSync(input, 'utf8');
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(`读取文件失败: ${e.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let compressed;
|
|
31
|
+
try {
|
|
32
|
+
compressed = parse(text);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.error(`解析失败: ${e.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const data = decompress(compressed);
|
|
39
|
+
fs.writeFileSync(output, JSON.stringify(data, null, 2), 'utf8');
|
|
40
|
+
|
|
41
|
+
console.log(`输入: ${path.basename(input)}`);
|
|
42
|
+
console.log(`输出: ${path.basename(output)} (${data.length} 条)`);
|
package/esm.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
const require = createRequire(import.meta.url);
|
|
3
|
-
const { compress, decompress, stringify, parse } = require('./compress.js');
|
|
4
|
-
export { compress, decompress, stringify, parse };
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
const { compress, decompress, stringify, parse } = require('./compress.js');
|
|
4
|
+
export { compress, decompress, stringify, parse };
|
|
5
|
+
export default { compress, decompress, stringify, parse };
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "slimjson",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"main": "compress.js",
|
|
5
|
-
"exports": {
|
|
6
|
-
".": {
|
|
7
|
-
"import": "./esm.mjs",
|
|
8
|
-
"require": "./compress.js"
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"test": "jest"
|
|
13
|
-
},
|
|
14
|
-
"author": "lastheaven",
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"description": "轻量级对象数组压缩工具",
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"jest": "^30.4.2"
|
|
19
|
-
},
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "https://github.com/LastHeaven/slimjson.git"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "slimjson",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"main": "compress.js",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./esm.mjs",
|
|
8
|
+
"require": "./compress.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "jest"
|
|
13
|
+
},
|
|
14
|
+
"author": "lastheaven",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"description": "轻量级对象数组压缩工具",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"jest": "^30.4.2"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/LastHeaven/slimjson.git"
|
|
23
|
+
}
|
|
24
|
+
}
|