-
Notifications
You must be signed in to change notification settings - Fork 67.1k
Expand file tree
/
Copy pathget-lintable-yml.ts
More file actions
executable file
·98 lines (89 loc) · 3.35 KB
/
get-lintable-yml.ts
File metadata and controls
executable file
·98 lines (89 loc) · 3.35 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
import yaml from 'js-yaml'
import fs from 'fs/promises'
import dataSchemas from '@/data-directory/lib/data-schemas/index'
import ajv from '@/tests/lib/validate-json-schema'
// AJV already has a built-in way to extract out properties
// with a specific keyword using a custom validator function.
// The intended purpose of the validator function is to perform
// validation of course, but we are overloading it here to extract
// the `lintable` properties and their parent path in the schema.
// mdDict contains the extracted `lintable` properties
// and their parent path in the schema.
//
// For example, assuming all items in `bar` are lintable,
// in this yaml file:
//
// foo:
// bar:
// - item 1
// - item 2
//
// mdDict will be populated with:
//
// { '/foo/bar/0': 'item 1', '/foo/bar/1': 'item 2' }
const mdDict = new Map<string, string>()
const lintableData: string[] = Object.keys(dataSchemas)
// To redefine a custom keyword, you must remove it
// then re-add it with the new definition. The default
// ajv instance defines the `lintable` keyword without
// a custom validator function.
ajv.removeKeyword('lintable')
ajv.addKeyword({
keyword: 'lintable',
type: 'string',
// For docs on defining validate see
// https://ajv.js.org/keywords.html#define-keyword-with-validate-function
validate: (
_compiled: boolean,
data: string,
_schema: unknown,
parentInfo?: { instancePath: string },
): boolean => {
if (parentInfo) mdDict.set(parentInfo.instancePath, data)
return true
},
errors: false,
})
// We do want to validate the value of each `lintable`
// property when running the content linter test.
// Because we have multiple rules, we can't write a single
// validator function that will work for all `lintable`
// properties. So we extract the `lintable` properties
// out of the schema and run those values through each
// linter rule.
// We need to know how to correlate each extracted property
// back to the location in the original schema file,
// so we also need the parent path of the `lintable`
// property in the schema.
export async function getLintableYml(dataFilePath: string): Promise<Record<string, string> | null> {
const matchingDataPath = lintableData.find(
(ref) => dataFilePath === ref || dataFilePath.startsWith(ref),
)
if (!matchingDataPath) return null
const schemaFilePath = dataSchemas[matchingDataPath]
if (!schemaFilePath) return null
const schema = (await import(schemaFilePath)).default
if (!schema) return null
const data = yaml.load(await fs.readFile(dataFilePath, 'utf8'))
mdDict.clear()
// This validate function will call all keyword validator functions and populate mdDict
ajv.validate(schema, data)
return mdDict.size ? Object.fromEntries(addPathToKey(mdDict, dataFilePath)) : null
}
// The key is the parent's instance path, but we also need
// to include the data file path in the key to map the result
// back to a file in the data directory.
// The resulting key looks like:
// 'data/variables/product.yml /pat_v1_caps'
function addPathToKey(mdDictMap: Map<string, string>, dataFilePath: string): Map<string, string> {
const keys = Array.from(mdDictMap.keys())
for (const key of keys) {
const newKey = `${dataFilePath} ${key}`
const value = mdDictMap.get(key)
if (value !== undefined) {
mdDictMap.delete(key)
mdDictMap.set(newKey, value)
}
}
return mdDictMap
}