@01-edu/shared 1.0.5 → 1.0.6
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/bin/check-definitions.js +51 -19
- package/definitions-checker.js +15 -28
- package/package.json +1 -1
package/bin/check-definitions.js
CHANGED
|
@@ -1,31 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFile, stat } from 'node:fs/promises'
|
|
3
|
+
import { readFile, stat, watch } from 'node:fs/promises'
|
|
4
4
|
|
|
5
5
|
import { checkAndBuildDefinitions } from '../definitions-checker.js'
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
const isAudit = validation => validation.type.endsWith('_audit')
|
|
8
|
+
const readDef = async key => {
|
|
9
|
+
const path = key == null ? 'content/def.json' : `content/${key}/def.json`
|
|
10
|
+
const def = JSON.parse(await readFile(path, 'utf8'))
|
|
11
|
+
def.attrs || (def.attrs = {})
|
|
12
|
+
def.referencePath = path
|
|
13
|
+
|
|
14
|
+
switch (def.type) {
|
|
15
|
+
case 'project':
|
|
16
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We want to fallthrough
|
|
17
|
+
case 'raid': {
|
|
18
|
+
const audit = (def.attrs.validations || []).find(isAudit)
|
|
19
|
+
if (!audit) {
|
|
20
|
+
throw Error('project and raid must have an audit validation specified')
|
|
21
|
+
}
|
|
22
|
+
if (audit.form) {
|
|
23
|
+
throw Error(
|
|
24
|
+
'audit form attribute is automatically set to be ./audit/README.md, do not specify it',
|
|
25
|
+
)
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
audit.form = `content/${key}/audit/README.md`
|
|
28
|
+
await stat(audit.form)
|
|
29
|
+
}
|
|
30
|
+
case 'exercise': {
|
|
31
|
+
if (def.attrs.subject) {
|
|
32
|
+
throw Error(
|
|
33
|
+
'subject attribute is automatically set to be ./README.md, do not specify it',
|
|
34
|
+
)
|
|
22
35
|
}
|
|
36
|
+
def.attrs.subject = `content/${key}/README.md`
|
|
37
|
+
await stat(def.attrs.subject)
|
|
23
38
|
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return def
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const runChecks = async () => {
|
|
45
|
+
try {
|
|
46
|
+
return await checkAndBuildDefinitions(readDef)
|
|
47
|
+
} catch ({ message, stack, ...props }) {
|
|
48
|
+
console.error(message)
|
|
49
|
+
console.error(props)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
24
52
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
if (process.argv.includes('--watch') || process.argv.includes('-w')) {
|
|
54
|
+
await runChecks()
|
|
55
|
+
for await (const event of watch('.', { recursive: true })) {
|
|
56
|
+
console.clear()
|
|
57
|
+
console.log(event.eventType, 'on', event.filename, '\n')
|
|
58
|
+
await runChecks()
|
|
59
|
+
}
|
|
60
|
+
} else if (!(await runChecks())) {
|
|
29
61
|
process.exit(1)
|
|
30
62
|
}
|
|
31
63
|
|
package/definitions-checker.js
CHANGED
|
@@ -31,7 +31,7 @@ const assertDef = def => {
|
|
|
31
31
|
if (refId && typeof refId !== 'number') throw Error(`Invalid refId property`)
|
|
32
32
|
if (childrenAttrs) throw Error(`childrenAttrs is no longer supported`)
|
|
33
33
|
|
|
34
|
-
for (const
|
|
34
|
+
for (const key of Object.keys(attrs)) {
|
|
35
35
|
if (relationAttributes[key]) {
|
|
36
36
|
throw Error(
|
|
37
37
|
`Attr ${key} should be defined in the relation with its parent, not on the object itself.`,
|
|
@@ -89,7 +89,7 @@ const assertRelation = (parent, key, relation) => {
|
|
|
89
89
|
throw Error(`Self reference in child ${key}`)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
for (const
|
|
92
|
+
for (const key of Object.keys(relation)) {
|
|
93
93
|
if (key === 'ref') continue
|
|
94
94
|
const matches = attributes[key]
|
|
95
95
|
if (matches?.[type]?.restrictive) {
|
|
@@ -138,29 +138,6 @@ const checkAttrs = object => {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// TODO: replace by Set.intersection once available in node
|
|
142
|
-
const intersection = (a, b) => {
|
|
143
|
-
const result = new Set()
|
|
144
|
-
for (const element of a) {
|
|
145
|
-
b.has(element) && result.add(element)
|
|
146
|
-
}
|
|
147
|
-
return result
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const generatePairs = arr => {
|
|
151
|
-
const result = []
|
|
152
|
-
let i = -1
|
|
153
|
-
// Only go up to the second to last element
|
|
154
|
-
while (++i < arr.length - 1) {
|
|
155
|
-
let j = i
|
|
156
|
-
// Start from the next element to avoid duplicates
|
|
157
|
-
while (++j < arr.length) {
|
|
158
|
-
result.push([arr[i], arr[j]])
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return result
|
|
162
|
-
}
|
|
163
|
-
|
|
164
141
|
const isExam = def => def.type === 'exam'
|
|
165
142
|
const assertExams = definitions => {
|
|
166
143
|
const groups = {}
|
|
@@ -197,7 +174,13 @@ const assertExams = definitions => {
|
|
|
197
174
|
export const checkAndBuildDefinitions = async readDef => {
|
|
198
175
|
const cache = {}
|
|
199
176
|
const getDefs = async ([key, relation]) => {
|
|
200
|
-
|
|
177
|
+
let def
|
|
178
|
+
try {
|
|
179
|
+
def = cache[key] || (cache[key] = await readDef(key))
|
|
180
|
+
} catch (err) {
|
|
181
|
+
err.name = key
|
|
182
|
+
throw err
|
|
183
|
+
}
|
|
201
184
|
try {
|
|
202
185
|
assertDef(def)
|
|
203
186
|
} catch (err) {
|
|
@@ -207,7 +190,12 @@ export const checkAndBuildDefinitions = async readDef => {
|
|
|
207
190
|
}
|
|
208
191
|
relation && (relation.ref = def)
|
|
209
192
|
const relations = Object.entries(def.children || {}).map(getDefs)
|
|
210
|
-
|
|
193
|
+
try {
|
|
194
|
+
return [def, ...(await Promise.all(relations)).flat()]
|
|
195
|
+
} catch (err) {
|
|
196
|
+
key && (err.parents || (err.parents = [])).push(key)
|
|
197
|
+
throw err
|
|
198
|
+
}
|
|
211
199
|
}
|
|
212
200
|
|
|
213
201
|
const definitions = await getDefs([])
|
|
@@ -215,7 +203,6 @@ export const checkAndBuildDefinitions = async readDef => {
|
|
|
215
203
|
// Assert all relations looks ok, possible child / parent, allowed attributes
|
|
216
204
|
for (const def of definitions) {
|
|
217
205
|
if (!def.children) continue
|
|
218
|
-
const allowedTypes = childTypes[def.type]
|
|
219
206
|
try {
|
|
220
207
|
for (const entry of Object.entries(def.children)) {
|
|
221
208
|
assertRelation(def, entry[0], entry[1])
|