-
Notifications
You must be signed in to change notification settings - Fork 8
/
strip.js
98 lines (93 loc) · 2.72 KB
/
strip.js
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
import isEmpty from 'lodash/isEmpty.js'
import unset from 'lodash/fp/unset.js'
import jsonPointer from 'json-pointer'
import validate from './validate.js'
import sortObjectKeys from './shared/sortObjectKeys.js'
const { parse } = jsonPointer
/**
* @param {Array<import('./shared/types.js').DocumentTest>} tests
* @param {any} document
*/
export default async function (tests, document) {
/** @type {Array<{ instancePath: string; message: string; error: boolean }>} */
const strippedPaths = []
/**
* @param {{}} doc
* @param {string} instancePath
* @returns {{}}
*/
const deleteEmptyNodes = (doc, instancePath) => {
if (typeof doc === 'string' || typeof doc === 'number') return doc
if (Array.isArray(doc))
return doc.map((item, i) =>
deleteEmptyNodes(item, `${instancePath}/${i}`)
)
return {
...Object.fromEntries(
Object.entries(doc)
.filter(([key, value]) => {
const valueIsEmpty =
value === '' ||
value === null ||
(typeof value === 'object' && isEmpty(value))
if (valueIsEmpty) {
strippedPaths.push({
instancePath: `${instancePath}/${key}`,
message: 'value was empty',
error: false,
})
}
return !valueIsEmpty
})
.map(([key, value]) => [
key,
deleteEmptyNodes(value, `${instancePath}/${key}`),
])
),
}
}
const documentWithoutEmptyNodes = deleteEmptyNodes(document, '')
let errorStrippedDocument = documentWithoutEmptyNodes
/**
* @type {Array<{
* message?: string
* instancePath: string
* }>}
*/
let errors
do {
errors = (await validate(tests, errorStrippedDocument)).tests.flatMap(
(t) => t.errors
)
errorStrippedDocument = JSON.parse(
JSON.stringify(
errors.reduce((updatedDoc, error) => {
strippedPaths.push({
instancePath: error.instancePath,
error: true,
message: /** @type {string} */ (error.message),
})
const parsedInstancePath = parse(error.instancePath).join('.')
if (parsedInstancePath === '') return {}
return unset(parsedInstancePath, updatedDoc)
}, errorStrippedDocument),
(_, value) => {
if (Array.isArray(value)) {
return value.filter((e) => e !== undefined)
}
return value
}
)
)
} while (
errors &&
errors.length &&
Object.keys(errorStrippedDocument).length > 0
)
return {
document: /** @type {any} */ (
sortObjectKeys(new Intl.Collator(), errorStrippedDocument)
),
strippedPaths,
}
}