Skip to content

Commit 8d236bf

Browse files
committed
feat: switch collections to JSON schema
- Add VS Code support for autocomplete in collections
1 parent a71d3da commit 8d236bf

File tree

7 files changed

+201
-98
lines changed

7 files changed

+201
-98
lines changed

.vscode/settings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,11 @@
2929
"[html]": {
3030
"editor.defaultFormatter": "biomejs.biome"
3131
},
32-
"dbmlERDPreviewer.preferredTheme": "dark"
32+
"dbmlERDPreviewer.preferredTheme": "dark",
33+
"json.schemas": [
34+
{
35+
"fileMatch": ["collections/assets/**/*.json"],
36+
"url": "./collections/$schemas/assets.json"
37+
}
38+
]
3339
}

collections/$schemas/assets.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "assets.json",
4+
"title": "Asset",
5+
"description": "An asset in the collection",
6+
"definitions": {
7+
"Source": {
8+
"type": "object",
9+
"required": ["authors", "title", "publisher", "publication"],
10+
"properties": {
11+
"authors": {
12+
"type": "array",
13+
"items": { "$ref": "common.json#/definitions/Person" }
14+
},
15+
"translators": {
16+
"type": "array",
17+
"items": { "$ref": "common.json#/definitions/Person" }
18+
},
19+
"title": { "type": "string" },
20+
"subtitle": { "type": "string" },
21+
"edition": { "type": "string" },
22+
"publisher": { "$ref": "common.json#/definitions/Publisher" },
23+
"publication": { "$ref": "common.json#/definitions/Publication" },
24+
"url": { "type": "string" },
25+
"accessed": { "type": "string" },
26+
"volume": { "$ref": "common.json#/definitions/Volume" },
27+
"type": { "type": "string" },
28+
"language": { "type": "string" },
29+
"isbn": { "type": "string" },
30+
"doi": { "type": "string" },
31+
"pages": { "type": "string" },
32+
"notes": { "type": "string" }
33+
}
34+
}
35+
},
36+
"type": "object",
37+
"required": ["name", "source"],
38+
"properties": {
39+
"name": { "$ref": "common.json#/definitions/Iso15924" },
40+
"source": { "$ref": "#/definitions/Source" }
41+
}
42+
}

collections/$schemas/common.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "common.json",
4+
"definitions": {
5+
"Iso15924": {
6+
"type": "object",
7+
"required": ["Latn", "Guru"],
8+
"properties": {
9+
"Latn": { "type": "string" },
10+
"Guru": { "type": "string" }
11+
}
12+
},
13+
"Person": {
14+
"type": "object",
15+
"required": ["first", "last"],
16+
"properties": {
17+
"first": { "type": "string" },
18+
"middle": { "type": "string" },
19+
"last": { "type": "string" },
20+
"prefix": { "type": "string" },
21+
"suffix": { "type": "string" }
22+
}
23+
},
24+
"Publisher": {
25+
"type": "object",
26+
"required": ["name"],
27+
"properties": {
28+
"name": { "type": "string" },
29+
"city": { "type": "string" },
30+
"country": { "type": "string" }
31+
}
32+
},
33+
"Publication": {
34+
"type": "object",
35+
"required": ["date"],
36+
"properties": {
37+
"date": { "type": "string" },
38+
"version": { "type": "string" }
39+
}
40+
},
41+
"Volume": {
42+
"type": "object",
43+
"properties": {
44+
"current": { "type": "number" },
45+
"total": { "type": "number" }
46+
}
47+
}
48+
}
49+
}

package-lock.json

Lines changed: 57 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
"url": "git+https://github.com/ShabadOS/database.git"
2020
},
2121
"author": "Shabad OS Team <team@shabados.com>",
22-
"contributors": ["GurbaniNow Team <contact@gurbaninow.com> (https://gurbaninow.com)"],
22+
"contributors": [
23+
"GurbaniNow Team <contact@gurbaninow.com> (https://gurbaninow.com)"
24+
],
2325
"license": "MIT",
2426
"bugs": {
2527
"url": "https://github.com/ShabadOS/database/issues"
@@ -34,11 +36,11 @@
3436
"validate": "node --experimental-strip-types scripts/validate-collections.ts"
3537
},
3638
"dependencies": {
37-
"@types/node": "^22.13.9",
38-
"valibot": "^1.0.0-rc.3"
39+
"ajv": "^8.17.1"
3940
},
4041
"devDependencies": {
4142
"@biomejs/biome": "^1.9.4",
43+
"@types/node": "^22.13.9",
4244
"chalk": "^5.4.1",
4345
"dedent": "^1.5.3",
4446
"fast-glob": "^3.3.3"

scripts/validate-collections.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,62 @@
1-
import fg from 'fast-glob'
2-
import { type ObjectEntries, type ObjectSchema, safeParseAsync } from 'valibot'
3-
41
import { readFile } from 'node:fs/promises'
2+
import Ajv from 'ajv'
53
import chalk from 'chalk'
64
import dedent from 'dedent'
7-
import { AssetSchema } from '#~/types/collections'
5+
import fg from 'fast-glob'
6+
7+
const ajv = new Ajv({
8+
allErrors: true,
9+
})
10+
11+
const SCHEMA_PATH = './collections/$schemas'
12+
13+
const commonSchema = JSON.parse(await readFile(`${SCHEMA_PATH}/common.json`, 'utf-8'))
14+
ajv.addSchema(commonSchema)
15+
16+
const validateCollection = async (schemaFile: string, collectionName: string) => {
17+
console.log(
18+
chalk.green(`\nValidating ${collectionName} collection using ${schemaFile} schema...`),
19+
)
820

9-
const validateCollection = async (name: string, schema: ObjectSchema<ObjectEntries, undefined>) => {
10-
console.log(chalk.green(`\nValidating ${name} collection...`))
21+
const schemaContent = JSON.parse(await readFile(`${SCHEMA_PATH}/${schemaFile}`, 'utf-8'))
22+
const validate = ajv.compile(schemaContent)
1123

12-
const collectionPath = `./collections/${name}`
24+
const collectionPath = `./collections/${collectionName}`
1325
const collection = await fg(`${collectionPath}/**/*.json`)
1426

27+
let hasErrors = false
28+
1529
for (const filePath of collection) {
1630
const data = JSON.parse(await readFile(filePath, 'utf-8'))
17-
const result = await safeParseAsync(schema, data)
31+
const isValid = validate(data)
1832

1933
const fileName = filePath.replace(`${collectionPath}/`, '')
2034

21-
if (!result.success) {
35+
if (!isValid) {
36+
hasErrors = true
2237
console.error(
2338
chalk.red(
24-
dedent`Invalid ${name} document: ${fileName}
25-
${result.issues.map((issue) => `- ${issue.message}`).join('\n')}
39+
dedent`Invalid ${collectionName} document: ${fileName}
40+
${validate.errors?.map((error) => `- ${error.instancePath || '/'} ${error.message}`).join('\n')}
2641
`,
2742
),
2843
)
2944
}
3045
}
46+
47+
if (!hasErrors) {
48+
console.log(chalk.green(`✓ All ${collectionName} documents are valid`))
49+
}
50+
51+
return !hasErrors
52+
}
53+
54+
const validationConfigs = [{ schemaFile: 'assets.json', collectionName: 'assets' }]
55+
56+
let allSuccess = true
57+
for (const config of validationConfigs) {
58+
const success = await validateCollection(config.schemaFile, config.collectionName)
59+
if (!success) allSuccess = false
3160
}
3261

33-
await validateCollection('assets', AssetSchema)
62+
process.exit(allSuccess ? 0 : 1)

0 commit comments

Comments
 (0)