@thegetty/quire-cli 1.0.0-rc.37 → 1.0.0-rc.38

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.
Files changed (85) hide show
  1. package/package.json +3 -2
  2. package/src/commands/build.js +13 -6
  3. package/src/commands/clean.js +10 -2
  4. package/src/commands/clean.spec.js +10 -0
  5. package/src/commands/clean.test.js +122 -0
  6. package/src/commands/config.js +3 -1
  7. package/src/commands/config.test.js +42 -1
  8. package/src/commands/doctor.js +251 -0
  9. package/src/commands/doctor.spec.js +115 -0
  10. package/src/commands/doctor.test.js +1409 -0
  11. package/src/commands/epub.js +9 -2
  12. package/src/commands/info.js +79 -71
  13. package/src/commands/info.spec.js +8 -0
  14. package/src/commands/info.test.js +173 -76
  15. package/src/commands/pdf.js +10 -3
  16. package/src/commands/validate.js +39 -7
  17. package/src/commands/validate.spec.js +8 -0
  18. package/src/commands/validate.test.js +144 -0
  19. package/src/lib/README.md +77 -22
  20. package/src/lib/conf/README.md +6 -0
  21. package/src/lib/conf/build-status.js +103 -0
  22. package/src/lib/conf/build-status.test.js +247 -0
  23. package/src/lib/conf/defaults.js +14 -0
  24. package/src/lib/conf/format.js +1 -1
  25. package/src/lib/conf/schema.js +46 -1
  26. package/src/lib/constants.js +28 -0
  27. package/src/lib/doctor/README.md +667 -0
  28. package/src/lib/doctor/checks/environment/cli-version.js +62 -0
  29. package/src/lib/doctor/checks/environment/cli-version.test.js +132 -0
  30. package/src/lib/doctor/checks/environment/git-available.js +77 -0
  31. package/src/lib/doctor/checks/environment/git-available.test.js +52 -0
  32. package/src/lib/doctor/checks/environment/index.js +13 -0
  33. package/src/lib/doctor/checks/environment/node-version.js +66 -0
  34. package/src/lib/doctor/checks/environment/node-version.test.js +17 -0
  35. package/src/lib/doctor/checks/environment/npm-available.js +66 -0
  36. package/src/lib/doctor/checks/environment/npm-available.test.js +52 -0
  37. package/src/lib/doctor/checks/environment/os-info.js +48 -0
  38. package/src/lib/doctor/checks/environment/os-info.test.js +81 -0
  39. package/src/lib/doctor/checks/environment/runtime-info.js +51 -0
  40. package/src/lib/doctor/checks/environment/runtime-info.test.js +132 -0
  41. package/src/lib/doctor/checks/outputs/epub-output.js +119 -0
  42. package/src/lib/doctor/checks/outputs/epub-output.test.js +277 -0
  43. package/src/lib/doctor/checks/outputs/index.js +10 -0
  44. package/src/lib/doctor/checks/outputs/pdf-output.js +144 -0
  45. package/src/lib/doctor/checks/outputs/pdf-output.test.js +377 -0
  46. package/src/lib/doctor/checks/outputs/stale-build.js +122 -0
  47. package/src/lib/doctor/checks/outputs/stale-build.test.js +282 -0
  48. package/src/lib/doctor/checks/project/data-files.js +56 -0
  49. package/src/lib/doctor/checks/project/data-files.test.js +125 -0
  50. package/src/lib/doctor/checks/project/dependencies.js +53 -0
  51. package/src/lib/doctor/checks/project/dependencies.test.js +71 -0
  52. package/src/lib/doctor/checks/project/index.js +11 -0
  53. package/src/lib/doctor/checks/project/quire-11ty.js +98 -0
  54. package/src/lib/doctor/checks/project/quire-11ty.test.js +170 -0
  55. package/src/lib/doctor/checks/project/quire-project.js +38 -0
  56. package/src/lib/doctor/checks/project/quire-project.test.js +47 -0
  57. package/src/lib/doctor/checks/tools/index.js +10 -0
  58. package/src/lib/doctor/checks/tools/pandoc-available.js +82 -0
  59. package/src/lib/doctor/checks/tools/pandoc-available.test.js +73 -0
  60. package/src/lib/doctor/checks/tools/prince-available.js +81 -0
  61. package/src/lib/doctor/checks/tools/prince-available.test.js +73 -0
  62. package/src/lib/doctor/constants.js +39 -0
  63. package/src/lib/doctor/formatDuration.js +108 -0
  64. package/src/lib/doctor/formatDuration.test.js +76 -0
  65. package/src/lib/doctor/formatters/human.js +257 -0
  66. package/src/lib/doctor/formatters/human.test.js +463 -0
  67. package/src/lib/doctor/formatters/index.js +8 -0
  68. package/src/lib/doctor/formatters/json.js +78 -0
  69. package/src/lib/doctor/formatters/json.test.js +174 -0
  70. package/src/lib/doctor/formatters/shared.js +129 -0
  71. package/src/lib/doctor/formatters/shared.test.js +194 -0
  72. package/src/lib/doctor/index.js +271 -0
  73. package/src/lib/doctor/index.test.js +797 -0
  74. package/src/lib/platform.js +95 -0
  75. package/src/lib/project/build.js +44 -25
  76. package/src/lib/project/build.test.js +30 -8
  77. package/src/lib/project/detect.js +16 -2
  78. package/src/lib/project/index.js +18 -2
  79. package/src/lib/project/output-paths.js +87 -0
  80. package/src/lib/project/output-paths.test.js +66 -0
  81. package/src/lib/project/paths.js +48 -0
  82. package/src/main.js +24 -2
  83. package/src/packageConfig.js +17 -0
  84. package/src/validators/validate-data-files.js +154 -0
  85. package/src/validators/validate-data-files.test.js +217 -0
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Doctor module - diagnostic checks for Quire environment
3
+ *
4
+ * @module lib/doctor
5
+ */
6
+ import createDebug from '#debug'
7
+
8
+ // Import checks from domain submodules
9
+ import {
10
+ checkOsInfo,
11
+ checkCliVersion,
12
+ checkNodeVersion,
13
+ checkRuntimeInfo,
14
+ checkNpmAvailable,
15
+ checkGitAvailable,
16
+ } from './checks/environment/index.js'
17
+
18
+ import {
19
+ checkPrinceAvailable,
20
+ checkPandocAvailable,
21
+ } from './checks/tools/index.js'
22
+
23
+ import {
24
+ checkQuireProject,
25
+ checkDependencies,
26
+ checkOutdatedQuire11ty,
27
+ checkDataFiles,
28
+ } from './checks/project/index.js'
29
+
30
+ import {
31
+ checkStaleBuild,
32
+ checkPdfOutput,
33
+ checkEpubOutput,
34
+ } from './checks/outputs/index.js'
35
+
36
+ // Re-export constants
37
+ export { DOCS_BASE_URL, REQUIRED_NODE_VERSION, QUIRE_11TY_PACKAGE } from './constants.js'
38
+
39
+ // Re-export individual checks
40
+ export {
41
+ // Environment
42
+ checkOsInfo,
43
+ checkCliVersion,
44
+ checkNodeVersion,
45
+ checkRuntimeInfo,
46
+ checkNpmAvailable,
47
+ checkGitAvailable,
48
+ // Tools
49
+ checkPrinceAvailable,
50
+ checkPandocAvailable,
51
+ // Project
52
+ checkQuireProject,
53
+ checkDependencies,
54
+ checkOutdatedQuire11ty,
55
+ checkDataFiles,
56
+ // Outputs
57
+ checkStaleBuild,
58
+ checkPdfOutput,
59
+ checkEpubOutput,
60
+ }
61
+
62
+ const debug = createDebug('lib:doctor')
63
+
64
+ /**
65
+ * Default timeout for async checks (in milliseconds)
66
+ * Prevents hanging checks from blocking the doctor command
67
+ */
68
+ export const DEFAULT_CHECK_TIMEOUT = 10_000 // 10 seconds
69
+
70
+ /**
71
+ * Check result type
72
+ * @typedef {Object} CheckResult
73
+ * @property {boolean} ok - Whether the check passed
74
+ * @property {'error'|'warn'|'na'|'timeout'} [level] - Severity level (default: 'error')
75
+ * @property {string|null} message - Optional message with details
76
+ * @property {string|null} [details] - Additional details (shown in verbose mode)
77
+ * @property {string|null} [remediation] - Steps to fix the issue (when ok is false)
78
+ * @property {string|null} [docsUrl] - Link to relevant documentation (when ok is false)
79
+ */
80
+
81
+ /**
82
+ * Run a check function with a timeout
83
+ * @param {Function} checkFn - The check function to run
84
+ * @param {string} checkId - ID of the check (for error messages)
85
+ * @param {number} [timeout=DEFAULT_CHECK_TIMEOUT] - Timeout in milliseconds
86
+ * @returns {Promise<CheckResult>}
87
+ */
88
+ async function runCheckWithTimeout(checkFn, checkId, timeout = DEFAULT_CHECK_TIMEOUT) {
89
+ let timeoutId
90
+
91
+ const timeoutPromise = new Promise((_, reject) => {
92
+ timeoutId = setTimeout(() => {
93
+ reject(new Error(`Check '${checkId}' timed out after ${timeout}ms`))
94
+ }, timeout)
95
+ })
96
+
97
+ try {
98
+ return await Promise.race([checkFn(), timeoutPromise])
99
+ } catch (error) {
100
+ debug('Check %s failed with error: %s', checkId, error.message)
101
+ return {
102
+ ok: false,
103
+ level: 'timeout',
104
+ message: `skipped (timed out after ${timeout}ms)`,
105
+ remediation: `The '${checkId}' check is taking too long to complete. This may indicate a network issue or a problem with the tool being checked.`,
106
+ }
107
+ } finally {
108
+ clearTimeout(timeoutId)
109
+ }
110
+ }
111
+
112
+ /**
113
+ * All available diagnostic checks organized by section
114
+ * Each check has an `id` for CLI filtering (short, lowercase, hyphenated)
115
+ */
116
+ export const checkSections = [
117
+ {
118
+ name: 'Environment',
119
+ checks: [
120
+ { id: 'os', name: 'Operating system', check: checkOsInfo },
121
+ { id: 'cli', name: 'Quire CLI version', check: checkCliVersion },
122
+ { id: 'node', name: 'Node.js version', check: checkNodeVersion },
123
+ { id: 'runtime', name: 'Node.js runtime', check: checkRuntimeInfo },
124
+ { id: 'npm', name: 'npm', check: checkNpmAvailable },
125
+ { id: 'git', name: 'Git', check: checkGitAvailable },
126
+ ],
127
+ },
128
+ {
129
+ name: 'Tools',
130
+ checks: [
131
+ { id: 'prince', name: 'PrinceXML', check: checkPrinceAvailable },
132
+ { id: 'pandoc', name: 'Pandoc', check: checkPandocAvailable },
133
+ ],
134
+ },
135
+ {
136
+ name: 'Project',
137
+ checks: [
138
+ { id: 'project', name: 'Quire project', check: checkQuireProject },
139
+ { id: 'deps', name: 'Dependencies', check: checkDependencies },
140
+ { id: '11ty', name: 'quire-11ty version', check: checkOutdatedQuire11ty },
141
+ { id: 'data', name: 'Data files', check: checkDataFiles },
142
+ ],
143
+ },
144
+ {
145
+ name: 'Outputs',
146
+ checks: [
147
+ { id: 'build', name: 'Build status', check: checkStaleBuild },
148
+ { id: 'pdf', name: 'PDF output', check: checkPdfOutput },
149
+ { id: 'epub', name: 'EPUB output', check: checkEpubOutput },
150
+ ],
151
+ },
152
+ ]
153
+
154
+ /**
155
+ * Valid check IDs for filtering individual checks
156
+ */
157
+ export const CHECK_IDS = checkSections.flatMap((s) => s.checks.map((c) => c.id))
158
+
159
+ /**
160
+ * All available diagnostic checks (flat list for backwards compatibility)
161
+ */
162
+ export const checks = [
163
+ { name: 'Operating system', check: checkOsInfo },
164
+ { name: 'Quire CLI version', check: checkCliVersion },
165
+ { name: 'Node.js version', check: checkNodeVersion },
166
+ { name: 'Node.js runtime', check: checkRuntimeInfo },
167
+ { name: 'npm', check: checkNpmAvailable },
168
+ { name: 'Git', check: checkGitAvailable },
169
+ { name: 'PrinceXML', check: checkPrinceAvailable },
170
+ { name: 'Pandoc', check: checkPandocAvailable },
171
+ { name: 'Quire project', check: checkQuireProject },
172
+ { name: 'Dependencies', check: checkDependencies },
173
+ { name: 'quire-11ty version', check: checkOutdatedQuire11ty },
174
+ { name: 'Data files', check: checkDataFiles },
175
+ { name: 'Build status', check: checkStaleBuild },
176
+ { name: 'PDF output', check: checkPdfOutput },
177
+ { name: 'EPUB output', check: checkEpubOutput },
178
+ ]
179
+
180
+ /**
181
+ * Run all diagnostic checks (flat list)
182
+ * @returns {Promise<Array<{name: string, ok: boolean, message: string|null}>>}
183
+ */
184
+ export async function runAllChecks() {
185
+ debug('Running all diagnostic checks')
186
+ const results = []
187
+
188
+ for (const { name, check } of checks) {
189
+ const result = await check()
190
+ results.push({ name, ...result })
191
+ }
192
+
193
+ return results
194
+ }
195
+
196
+ /**
197
+ * Valid section names for filtering
198
+ */
199
+ export const SECTION_NAMES = ['environment', 'tools', 'project', 'outputs']
200
+
201
+ /**
202
+ * Run all diagnostic checks organized by section
203
+ * @param {Object} [options] - Filter options
204
+ * @param {string[]} [options.sections] - Optional array of section names to run (lowercase)
205
+ * @param {string[]} [options.checks] - Optional array of check IDs to run
206
+ * @param {number} [options.timeout] - Timeout in milliseconds for each check (default: DEFAULT_CHECK_TIMEOUT)
207
+ * @returns {Promise<Array<{section: string, results: Array<{id: string, name: string, ok: boolean, message: string|null}>}>>}
208
+ */
209
+ export async function runAllChecksWithSections(options = {}) {
210
+ const {
211
+ sections: filterSections = null,
212
+ checks: filterChecks = null,
213
+ timeout = DEFAULT_CHECK_TIMEOUT,
214
+ } = options
215
+
216
+ // Handle legacy call signature: runAllChecksWithSections(['environment'])
217
+ const sectionsFilter = Array.isArray(options) ? options : filterSections
218
+
219
+ debug('Running diagnostic checks with sections, filter: %o, checks: %o, timeout: %d', sectionsFilter, filterChecks, timeout)
220
+ const sections = []
221
+
222
+ for (const { name: sectionName, checks: sectionChecks } of checkSections) {
223
+ // Skip sections not in filter (if section filter is provided and no check filter)
224
+ if (sectionsFilter && !filterChecks && !sectionsFilter.includes(sectionName.toLowerCase())) {
225
+ continue
226
+ }
227
+
228
+ const results = []
229
+ for (const { id, name, check } of sectionChecks) {
230
+ // Skip checks not in filter (if check filter is provided)
231
+ if (filterChecks && !filterChecks.includes(id)) {
232
+ continue
233
+ }
234
+
235
+ const result = await runCheckWithTimeout(check, id, timeout)
236
+ results.push({ id, name, ...result })
237
+ }
238
+
239
+ // Only include section if it has results
240
+ if (results.length > 0) {
241
+ sections.push({ section: sectionName, results })
242
+ }
243
+ }
244
+
245
+ return sections
246
+ }
247
+
248
+ export default {
249
+ checks,
250
+ checkSections,
251
+ checkCliVersion,
252
+ checkDataFiles,
253
+ checkDependencies,
254
+ checkEpubOutput,
255
+ checkGitAvailable,
256
+ checkNodeVersion,
257
+ checkNpmAvailable,
258
+ checkOsInfo,
259
+ checkOutdatedQuire11ty,
260
+ checkPdfOutput,
261
+ checkPandocAvailable,
262
+ checkPrinceAvailable,
263
+ checkRuntimeInfo,
264
+ checkQuireProject,
265
+ checkStaleBuild,
266
+ runAllChecks,
267
+ runAllChecksWithSections,
268
+ CHECK_IDS,
269
+ SECTION_NAMES,
270
+ DEFAULT_CHECK_TIMEOUT,
271
+ }