@thegetty/quire-cli 1.0.0-rc.33 → 1.0.0-rc.35
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/package.json +2 -2
- package/schemas/config.schema.json +194 -0
- package/schemas/figures.schema.json +56 -0
- package/schemas/layout.schema.json +5 -0
- package/schemas/objects.schema.json +88 -0
- package/schemas/publication.schema.json +162 -0
- package/schemas/references.schema.json +30 -0
- package/src/commands/validate.js +64 -0
- package/src/errors/validation/validation-error.js +13 -0
- package/src/errors/validation/yaml-duplicate-error.js +11 -0
- package/src/errors/validation/yaml-parse-error.js +11 -0
- package/src/errors/validation/yaml-validation-error.js +11 -0
- package/src/validators/utils.js +98 -0
- package/src/validators/validate-yaml.js +38 -0
- package/patches/.DS_Store +0 -0
- package/patches/README.md +0 -19
- package/patches/install-npm-version+1.0.9.patch +0 -12119
- package/src/.DS_Store +0 -0
- package/src/helpers/.DS_Store +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import YamlDuplicateIdError from '../errors/validation/yaml-duplicate-error.js'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { projectRoot } from '#lib/11ty/index.js'
|
|
6
|
+
|
|
7
|
+
const IMAGE_KEYS = new Set(['src', 'image', 'logo'])
|
|
8
|
+
|
|
9
|
+
export function getSchemaForDocument(file) {
|
|
10
|
+
const schemaName = path.basename(file, path.extname(file))
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const schemaPath = path.join(__dirname,'..','..','schemas', `${schemaName}.schema.json`)
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(fs.readFileSync(schemaPath, 'utf8'))
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.warn(`Warning: No schema found for document ${schemaName} at path: ${schemaPath}.`)
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Recursive helper to check image paths in nested figure list structures
|
|
23
|
+
function collectImagePaths(node, paths=[]) {
|
|
24
|
+
if(!node || typeof node !== 'object') return
|
|
25
|
+
|
|
26
|
+
for (const key in node) {
|
|
27
|
+
const value = node[key]
|
|
28
|
+
if (IMAGE_KEYS.has(key) && typeof value === 'string') {
|
|
29
|
+
paths.push(value)
|
|
30
|
+
}
|
|
31
|
+
collectImagePaths(value, paths)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return paths
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function validateImage(label, src) {
|
|
38
|
+
if(!src) return
|
|
39
|
+
|
|
40
|
+
let assetPath = ''
|
|
41
|
+
if(src.endsWith('.html')) {
|
|
42
|
+
assetPath = path.join(projectRoot, 'content', '_assets', src)
|
|
43
|
+
} else {
|
|
44
|
+
assetPath = path.join(projectRoot, 'content', '_assets', 'images', src)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if(!fs.existsSync(assetPath)) {
|
|
48
|
+
console.warn(`Warning: ${label} source not found at path: ${assetPath}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function validateImagePaths(doc) {
|
|
53
|
+
validateImage('Cover image', doc?.epub?.defaultCoverImage)
|
|
54
|
+
validateImage('Promo image', doc?.promo_image)
|
|
55
|
+
|
|
56
|
+
for (const figure of doc?.figure_list || []) {
|
|
57
|
+
let paths = []
|
|
58
|
+
const imagePaths = collectImagePaths(figure, paths)
|
|
59
|
+
for (const imgPath of imagePaths) {
|
|
60
|
+
validateImage(`Figure id ${figure.id}`, imgPath)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const publisher of doc?.publisher || []) {
|
|
65
|
+
validateImage('Logo', publisher.logo)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const contributor of doc?.contributor || []) {
|
|
69
|
+
validateImage('Contributor', contributor.image)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Lifted from packages/11ty/_plugins/globalData
|
|
75
|
+
* Throws an error if data contains duplicate ids
|
|
76
|
+
* @param {Object|Array} data
|
|
77
|
+
*/
|
|
78
|
+
export const checkForDuplicateIds = function (data, file) {
|
|
79
|
+
if (!data) return
|
|
80
|
+
|
|
81
|
+
if (Array.isArray(data)) {
|
|
82
|
+
if (data.every((item) => Object.hasOwn(item, 'id'))) {
|
|
83
|
+
const duplicates = data.filter((a, index) => {
|
|
84
|
+
return index !== data.findIndex((b) => b.id === a.id)
|
|
85
|
+
})
|
|
86
|
+
if (duplicates.length) {
|
|
87
|
+
const ids = duplicates.map(({ id }) => id)
|
|
88
|
+
throw new YamlDuplicateIdError(file, `Error in ${file}: Duplicate IDs found: ${ids.join(', ')}`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof data === 'object') {
|
|
94
|
+
Object.keys(data).forEach((key) => {
|
|
95
|
+
checkForDuplicateIds(data[key], file)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Ajv from 'ajv'
|
|
2
|
+
import addFormats from 'ajv-formats'
|
|
3
|
+
import { validateImagePaths ,getSchemaForDocument, checkForDuplicateIds } from './utils.js'
|
|
4
|
+
import YamlValidationError from '../errors/validation/yaml-validation-error.js'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import yaml from 'js-yaml'
|
|
7
|
+
|
|
8
|
+
export default function yamlValidation(file) {
|
|
9
|
+
|
|
10
|
+
const fileContent = fs.readFileSync(file, 'utf8')
|
|
11
|
+
|
|
12
|
+
let doc
|
|
13
|
+
try {
|
|
14
|
+
doc = yaml.load(fileContent)
|
|
15
|
+
} catch (error) {
|
|
16
|
+
const message = `Error in ${file}: ${error.reason} at line ${error.mark.line} column ${error.mark.column}`
|
|
17
|
+
throw new YamlValidationError(file, `${message}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const schema = getSchemaForDocument(file)
|
|
21
|
+
if(!schema){ return }
|
|
22
|
+
|
|
23
|
+
const ajv = new Ajv({allErrors:true})
|
|
24
|
+
addFormats(ajv)
|
|
25
|
+
const validate = ajv.compile(schema)
|
|
26
|
+
const valid = validate(doc)
|
|
27
|
+
if(!valid) {
|
|
28
|
+
const messages = validate.errors
|
|
29
|
+
.map(err => `${err.instancePath || '(root)'} ${err.message}`)
|
|
30
|
+
.join('\n')
|
|
31
|
+
|
|
32
|
+
const fullMessage = `Error in ${file}:\n${messages}`
|
|
33
|
+
throw new YamlValidationError(file, fullMessage)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
validateImagePaths(doc)
|
|
37
|
+
checkForDuplicateIds(doc, file)
|
|
38
|
+
}
|
package/patches/.DS_Store
DELETED
|
Binary file
|
package/patches/README.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
## Quire CLI Patches
|
|
2
|
-
|
|
3
|
-
Patches in this directory are applied to locally installed `node_modules` by the npm `postinstall` script ([npm scripts docs](https://docs.npmjs.com/cli/v10/using-npm/scripts)).
|
|
4
|
-
|
|
5
|
-
See [`patch-package`](https://github.com/ds300/patch-package) documenation for [making](https://github.com/ds300/patch-package#making-patches), [updating](https://github.com/ds300/patch-package#updating-patches), [applying](https://github.com/ds300/patch-package#applying-patches) and [reversing](https://github.com/ds300/patch-package#applying-patches) patches.
|
|
6
|
-
|
|
7
|
-
*Nota bene*
|
|
8
|
-
|
|
9
|
-
Patching the `quire-cli` package dependencies changes the way in which it must be must be installed. When installing the `quire-cli` as a _local package dependency_ the `--install-strategy=nested` flag must be used as follows
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm install quire-cli --install-strategy=nested
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Installing the `quire-cli` as a global npm module has not changed,
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
npm install quire-cli --global
|
|
19
|
-
```
|