@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.
@@ -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
- ```