@thegetty/quire-cli 1.0.0-rc.44 → 1.0.0-rc.46

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 (243) hide show
  1. package/CHANGELOG.md +0 -36
  2. package/README.md +0 -9
  3. package/bin/cli.js +1 -46
  4. package/package.json +9 -25
  5. package/src/Command.js +6 -26
  6. package/src/commands/README.md +122 -213
  7. package/src/commands/build.js +37 -45
  8. package/src/commands/clean.js +19 -33
  9. package/src/commands/conf.js +43 -0
  10. package/src/commands/create.js +14 -42
  11. package/src/commands/epub.js +31 -67
  12. package/src/commands/index.js +2 -9
  13. package/src/commands/info.js +86 -88
  14. package/src/commands/pdf.js +82 -63
  15. package/src/commands/preview.js +31 -32
  16. package/src/commands/validate.js +20 -64
  17. package/src/commands/version.js +43 -0
  18. package/src/errors/validation/validation-error.js +11 -20
  19. package/src/helpers/clean.js +1 -1
  20. package/src/helpers/is-quire.js +31 -0
  21. package/src/helpers/test-cwd.js +6 -5
  22. package/src/helpers/which.js +4 -10
  23. package/src/lib/11ty/README.md +19 -135
  24. package/src/lib/11ty/api.js +93 -235
  25. package/src/lib/11ty/cli.js +37 -112
  26. package/src/lib/11ty/index.js +5 -64
  27. package/src/lib/11ty/paths.js +103 -0
  28. package/src/lib/README.md +0 -330
  29. package/src/lib/conf/README.md +69 -86
  30. package/src/lib/conf/config.js +3 -5
  31. package/src/lib/conf/defaults.js +0 -65
  32. package/src/lib/conf/schema.js +9 -103
  33. package/src/lib/epub/README.md +2 -133
  34. package/src/lib/epub/epub.js +11 -36
  35. package/src/lib/epub/index.js +21 -111
  36. package/src/lib/epub/pandoc.js +4 -33
  37. package/src/lib/git/README.md +2 -151
  38. package/src/lib/git/index.js +13 -217
  39. package/src/lib/i18n/README.md +3 -0
  40. package/src/lib/i18n/config.js +53 -0
  41. package/src/lib/i18n/index.js +43 -0
  42. package/src/lib/i18n/localeService.js +58 -0
  43. package/src/lib/pdf/README.md +0 -131
  44. package/src/lib/pdf/index.js +21 -124
  45. package/src/lib/pdf/paged.js +52 -100
  46. package/src/lib/pdf/prince.js +37 -115
  47. package/src/lib/pdf/split.js +33 -61
  48. package/src/lib/quire/README.md +19 -0
  49. package/src/lib/quire/index.js +353 -0
  50. package/src/lib/reporter/README.md +2 -211
  51. package/src/lib/reporter/index.js +0 -607
  52. package/src/main.js +48 -221
  53. package/src/packageConfig.js +0 -17
  54. package/src/validators/utils.js +1 -2
  55. package/schemas/config.schema.json +0 -194
  56. package/schemas/figures.schema.json +0 -56
  57. package/schemas/layout.schema.json +0 -5
  58. package/schemas/objects.schema.json +0 -62
  59. package/schemas/publication.schema.json +0 -140
  60. package/schemas/references.schema.json +0 -29
  61. package/src/Command.spec.js +0 -99
  62. package/src/commands/build.spec.js +0 -113
  63. package/src/commands/build.test.js +0 -397
  64. package/src/commands/clean.spec.js +0 -118
  65. package/src/commands/clean.test.js +0 -382
  66. package/src/commands/config.js +0 -238
  67. package/src/commands/config.spec.js +0 -107
  68. package/src/commands/config.test.js +0 -845
  69. package/src/commands/create.spec.js +0 -112
  70. package/src/commands/create.test.js +0 -415
  71. package/src/commands/doctor.js +0 -251
  72. package/src/commands/doctor.spec.js +0 -115
  73. package/src/commands/doctor.test.js +0 -1409
  74. package/src/commands/epub.spec.js +0 -114
  75. package/src/commands/epub.test.js +0 -503
  76. package/src/commands/help.js +0 -60
  77. package/src/commands/help.test.js +0 -132
  78. package/src/commands/info.spec.js +0 -72
  79. package/src/commands/info.test.js +0 -512
  80. package/src/commands/pdf.spec.js +0 -114
  81. package/src/commands/pdf.test.js +0 -464
  82. package/src/commands/preview.spec.js +0 -97
  83. package/src/commands/preview.test.js +0 -250
  84. package/src/commands/use.js +0 -56
  85. package/src/commands/use.spec.js +0 -61
  86. package/src/commands/use.test.js +0 -280
  87. package/src/commands/validate.spec.js +0 -90
  88. package/src/commands/validate.test.js +0 -378
  89. package/src/commands/workflows.js +0 -70
  90. package/src/errors/build/build-failed-error.js +0 -19
  91. package/src/errors/build/config-field-missing-error.js +0 -20
  92. package/src/errors/build/config-file-not-found-error.js +0 -20
  93. package/src/errors/build/index.js +0 -11
  94. package/src/errors/help/help-topic-not-found-error.js +0 -30
  95. package/src/errors/help/index.js +0 -8
  96. package/src/errors/index.js +0 -58
  97. package/src/errors/input/index.js +0 -8
  98. package/src/errors/input/invalid-input-error.js +0 -21
  99. package/src/errors/install/dependency-install-error.js +0 -19
  100. package/src/errors/install/directory-not-empty-error.js +0 -25
  101. package/src/errors/install/index.js +0 -12
  102. package/src/errors/install/invalid-path-error.js +0 -31
  103. package/src/errors/install/invalid-starter-error.js +0 -27
  104. package/src/errors/install/version-not-found-error.js +0 -35
  105. package/src/errors/output/epub-generation-error.js +0 -19
  106. package/src/errors/output/index.js +0 -14
  107. package/src/errors/output/invalid-epub-library-error.js +0 -20
  108. package/src/errors/output/invalid-pdf-library-error.js +0 -20
  109. package/src/errors/output/missing-build-output-error.js +0 -21
  110. package/src/errors/output/pdf-generation-error.js +0 -24
  111. package/src/errors/output/tool-not-found-error.js +0 -37
  112. package/src/errors/project/index.js +0 -10
  113. package/src/errors/project/not-in-project-error.js +0 -20
  114. package/src/errors/project/project-create-error.js +0 -21
  115. package/src/errors/quire-error.js +0 -27
  116. package/src/errors/validation/index.js +0 -3
  117. package/src/errors/validation/unknown-config-key-error.js +0 -27
  118. package/src/errors/validation/unknown-config-operation-error.js +0 -24
  119. package/src/helpers/docs-url.js +0 -32
  120. package/src/helpers/pager.js +0 -59
  121. package/src/helpers/pager.test.js +0 -85
  122. package/src/helpers/suggest-similar.js +0 -124
  123. package/src/helpers/suggest-similar.test.js +0 -108
  124. package/src/helpers/test-cwd.test.js +0 -192
  125. package/src/lib/11ty/index.test.js +0 -655
  126. package/src/lib/commander/index.js +0 -103
  127. package/src/lib/commander/index.test.js +0 -86
  128. package/src/lib/commander/options.js +0 -237
  129. package/src/lib/commander/options.test.js +0 -205
  130. package/src/lib/conf/build-status.js +0 -103
  131. package/src/lib/conf/build-status.test.js +0 -247
  132. package/src/lib/conf/config.test.js +0 -281
  133. package/src/lib/conf/format.js +0 -60
  134. package/src/lib/conf/format.test.js +0 -106
  135. package/src/lib/conf/helpers.js +0 -91
  136. package/src/lib/conf/helpers.test.js +0 -136
  137. package/src/lib/conf/index.js +0 -22
  138. package/src/lib/constants.js +0 -28
  139. package/src/lib/doctor/README.md +0 -667
  140. package/src/lib/doctor/checks/environment/cli-version.js +0 -62
  141. package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
  142. package/src/lib/doctor/checks/environment/git-available.js +0 -77
  143. package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
  144. package/src/lib/doctor/checks/environment/index.js +0 -13
  145. package/src/lib/doctor/checks/environment/node-version.js +0 -66
  146. package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
  147. package/src/lib/doctor/checks/environment/npm-available.js +0 -66
  148. package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
  149. package/src/lib/doctor/checks/environment/os-info.js +0 -48
  150. package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
  151. package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
  152. package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
  153. package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
  154. package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
  155. package/src/lib/doctor/checks/outputs/index.js +0 -10
  156. package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
  157. package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
  158. package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
  159. package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
  160. package/src/lib/doctor/checks/project/data-files.js +0 -56
  161. package/src/lib/doctor/checks/project/data-files.test.js +0 -125
  162. package/src/lib/doctor/checks/project/dependencies.js +0 -53
  163. package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
  164. package/src/lib/doctor/checks/project/index.js +0 -11
  165. package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
  166. package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
  167. package/src/lib/doctor/checks/project/quire-project.js +0 -38
  168. package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
  169. package/src/lib/doctor/checks/tools/index.js +0 -10
  170. package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
  171. package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
  172. package/src/lib/doctor/checks/tools/prince-available.js +0 -81
  173. package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
  174. package/src/lib/doctor/constants.js +0 -39
  175. package/src/lib/doctor/formatDuration.js +0 -108
  176. package/src/lib/doctor/formatDuration.test.js +0 -76
  177. package/src/lib/doctor/formatters/human.js +0 -257
  178. package/src/lib/doctor/formatters/human.test.js +0 -463
  179. package/src/lib/doctor/formatters/index.js +0 -8
  180. package/src/lib/doctor/formatters/json.js +0 -78
  181. package/src/lib/doctor/formatters/json.test.js +0 -174
  182. package/src/lib/doctor/formatters/shared.js +0 -129
  183. package/src/lib/doctor/formatters/shared.test.js +0 -194
  184. package/src/lib/doctor/index.js +0 -271
  185. package/src/lib/doctor/index.test.js +0 -797
  186. package/src/lib/epub/engines.js +0 -46
  187. package/src/lib/epub/index.test.js +0 -518
  188. package/src/lib/epub/pandoc.test.js +0 -122
  189. package/src/lib/epub/schema.js +0 -21
  190. package/src/lib/error/README.md +0 -170
  191. package/src/lib/error/handler.js +0 -107
  192. package/src/lib/git/index.spec.js +0 -80
  193. package/src/lib/git/index.test.js +0 -453
  194. package/src/lib/help/frontmatter.js +0 -77
  195. package/src/lib/help/frontmatter.test.js +0 -139
  196. package/src/lib/help/index.js +0 -146
  197. package/src/lib/help/index.test.js +0 -188
  198. package/src/lib/help/topics/configuration.md +0 -77
  199. package/src/lib/help/topics/debugging.md +0 -69
  200. package/src/lib/help/topics/epub.md +0 -74
  201. package/src/lib/help/topics/pdf.md +0 -74
  202. package/src/lib/help/topics/publishing.md +0 -80
  203. package/src/lib/help/topics/workflows.md +0 -50
  204. package/src/lib/installer/index.js +0 -309
  205. package/src/lib/installer/index.spec.js +0 -83
  206. package/src/lib/installer/index.test.js +0 -545
  207. package/src/lib/logger/README.md +0 -424
  208. package/src/lib/logger/debug.js +0 -132
  209. package/src/lib/logger/debug.spec.js +0 -130
  210. package/src/lib/logger/debug.test.js +0 -128
  211. package/src/lib/logger/index.js +0 -228
  212. package/src/lib/logger/index.spec.js +0 -131
  213. package/src/lib/logger/index.test.js +0 -578
  214. package/src/lib/npm/README.md +0 -127
  215. package/src/lib/npm/index.js +0 -198
  216. package/src/lib/npm/index.spec.js +0 -60
  217. package/src/lib/npm/index.test.js +0 -355
  218. package/src/lib/pdf/engines.js +0 -46
  219. package/src/lib/pdf/index.test.js +0 -708
  220. package/src/lib/pdf/paged.test.js +0 -366
  221. package/src/lib/pdf/prince.test.js +0 -202
  222. package/src/lib/pdf/schema.js +0 -21
  223. package/src/lib/pdf/split.test.js +0 -445
  224. package/src/lib/platform.js +0 -95
  225. package/src/lib/process/manager.js +0 -110
  226. package/src/lib/process/manager.test.js +0 -55
  227. package/src/lib/project/build.js +0 -162
  228. package/src/lib/project/build.test.js +0 -275
  229. package/src/lib/project/config.js +0 -48
  230. package/src/lib/project/config.test.js +0 -134
  231. package/src/lib/project/detect.js +0 -47
  232. package/src/lib/project/detect.test.js +0 -157
  233. package/src/lib/project/index.js +0 -56
  234. package/src/lib/project/output-paths.js +0 -87
  235. package/src/lib/project/output-paths.test.js +0 -66
  236. package/src/lib/project/paths.js +0 -272
  237. package/src/lib/project/version.js +0 -110
  238. package/src/lib/project/version.test.js +0 -350
  239. package/src/lib/reporter/index.test.js +0 -862
  240. package/src/main.spec.js +0 -61
  241. package/src/main.test.js +0 -347
  242. package/src/validators/validate-data-files.js +0 -154
  243. package/src/validators/validate-data-files.test.js +0 -217
@@ -1,152 +1,3 @@
1
- # Git Façade
1
+ ## CLI Git Module
2
2
 
3
- A façade module that abstracts git command-line operations for the Quire CLI.
4
-
5
- ## Purpose
6
-
7
- This module provides a unified interface for git operations with:
8
-
9
- - Encapsulation of git implementation details
10
- - Consistent logging prefixed with `[CLI:lib/git]`
11
- - Unified error handling
12
- - Easy mockability for testing
13
-
14
- ## Usage
15
-
16
- ### Singleton (for global operations)
17
-
18
- ```javascript
19
- import git from '#lib/git/index.js'
20
-
21
- // Check git availability
22
- if (!git.isAvailable()) {
23
- console.error('git is not installed')
24
- }
25
-
26
- // Get git version
27
- // @see https://git-scm.com/docs/git-version
28
- const version = await git.version()
29
- ```
30
-
31
- ### Git Class (for repository-scoped operations)
32
-
33
- ```javascript
34
- import { Git } from '#lib/git/index.js'
35
-
36
- // Create instance with working directory
37
- const repo = new Git('/path/to/project')
38
-
39
- // Clone a repository
40
- // @see https://git-scm.com/docs/git-clone
41
- await repo.clone('https://github.com/user/repo', '.')
42
-
43
- // Initialize a repository
44
- // @see https://git-scm.com/docs/git-init
45
- await repo.init()
46
-
47
- // Stage files
48
- // @see https://git-scm.com/docs/git-add
49
- await repo.add('.')
50
- await repo.add(['file1.js', 'file2.js'])
51
-
52
- // Create a commit
53
- // @see https://git-scm.com/docs/git-commit
54
- await repo.commit('Initial commit')
55
-
56
- // Remove files from tracking
57
- // @see https://git-scm.com/docs/git-rm
58
- await repo.rm('package.json')
59
- await repo.rm(['file1.js', 'file2.js'])
60
- ```
61
-
62
- ## API
63
-
64
- ### Constructor
65
-
66
- #### `new Git(cwd)`
67
-
68
- Create a Git façade instance scoped to a working directory.
69
-
70
- **Parameters:**
71
- - `cwd` (string, optional) - Working directory for all operations
72
-
73
- ### Methods
74
-
75
- #### `isAvailable()`
76
-
77
- Check if git is available in PATH.
78
-
79
- #### `version()`
80
-
81
- Get the installed git version.
82
-
83
- #### `add(files)`
84
-
85
- Stage files for commit.
86
-
87
- **Parameters:**
88
- - `files` (string|string[]) - Files to stage (use `'.'` for all)
89
-
90
- #### `clone(url, destination)`
91
-
92
- Clone a repository.
93
-
94
- **Parameters:**
95
- - `url` (string) - Repository URL
96
- - `destination` (string, default: `'.'`) - Destination directory
97
-
98
- #### `commit(message)`
99
-
100
- Create a commit with staged changes.
101
-
102
- **Parameters:**
103
- - `message` (string) - Commit message
104
-
105
- #### `init()`
106
-
107
- Initialize a new git repository.
108
-
109
- #### `rm(files)`
110
-
111
- Remove files from the working tree and index.
112
-
113
- **Parameters:**
114
- - `files` (string|string[]) - Files to remove
115
-
116
- ## Testing
117
-
118
- ### Mocking the singleton
119
-
120
- ```javascript
121
- import esmock from 'esmock'
122
-
123
- const mockGit = {
124
- isAvailable: sandbox.stub().returns(true),
125
- version: sandbox.stub().resolves('2.39.0'),
126
- }
127
-
128
- const MyCommand = await esmock('./mycommand.js', {
129
- '#lib/git/index.js': { default: mockGit }
130
- })
131
- ```
132
-
133
- ### Mocking the Git class
134
-
135
- ```javascript
136
- import esmock from 'esmock'
137
-
138
- const MockGit = class {
139
- constructor(cwd) {
140
- this.cwd = cwd
141
- this.add = sandbox.stub().resolves()
142
- this.clone = sandbox.stub().resolves()
143
- this.commit = sandbox.stub().resolves()
144
- this.init = sandbox.stub().resolves()
145
- this.rm = sandbox.stub().resolves()
146
- }
147
- }
148
-
149
- const MyCommand = await esmock('./mycommand.js', {
150
- '#lib/git/index.js': { Git: MockGit }
151
- })
152
- ```
3
+ This `quire-cli/lib/git` module is a façade for interacting with `git` when creating new Quire projects and contributing to the `quire` code base, including the `quire-cli`.
@@ -1,226 +1,22 @@
1
- /**
2
- * Git façade providing abstracted git operations
3
- *
4
- * Provides a consistent interface for git commands with unified
5
- * logging, error handling, and testability.
6
- *
7
- * @example using default singleton for global operations
8
- * import git from '#lib/git/index.js'
9
- * const version = await git.version()
10
- * if (!git.isAvailable()) { ... }
11
- *
12
- * @example using Git class for repository-scoped operations
13
- * import { Git } from '#lib/git/index.js'
14
- * const repo = new Git('/path/to/project')
15
- * await repo.init()
16
- * await repo.add('.')
17
- * await repo.commit('Initial commit')
18
- *
19
- * @example mocking in tests
20
- * const mockGit = {
21
- * clone: sandbox.stub().resolves(),
22
- * commit: sandbox.stub().resolves(),
23
- * }
24
- * const MyCommand = await esmock('./mycommand.js', {
25
- * '#lib/git/index.js': { default: mockGit }
26
- * })
27
- *
28
- * @see https://git-scm.com/docs - git documentation
29
- * @module git
30
- */
31
- import { execa } from 'execa'
32
- import fs from 'node:fs'
33
- import path from 'node:path'
34
- import which from '#helpers/which.js'
35
- import createDebug from '#debug'
36
-
37
- const debug = createDebug('lib:git')
38
-
39
- /**
40
- * Check if a source string is a remote URL (vs local path)
41
- *
42
- * Git supports various remote URL formats:
43
- * - https://github.com/user/repo.git
44
- * - git@github.com:user/repo.git
45
- * - ssh://git@github.com/user/repo.git
46
- * - http://github.com/user/repo.git
47
- * - git://github.com/user/repo.git
48
- *
49
- * @param {string} source - Source string to check
50
- * @returns {boolean} True if source appears to be a remote URL
51
- */
52
- export function isRemoteUrl(source) {
53
- return source.startsWith('https://') ||
54
- source.startsWith('git@') ||
55
- source.startsWith('ssh://') ||
56
- source.startsWith('http://') ||
57
- source.startsWith('git://')
58
- }
1
+ import { simpleGit } from 'simple-git'
59
2
 
60
3
  /**
61
- * Check if a path is a git repository
62
- *
63
- * A directory is considered a git repository if it contains a .git directory.
64
- *
65
- * @param {string} dirPath - Path to check
66
- * @returns {boolean} True if the path is a git repository
4
+ * SimpleGit configuration
5
+ * @see https://github.com/steveukx/git-js#configuration
67
6
  */
68
- export function isGitRepository(dirPath) {
69
- const gitDir = path.join(dirPath, '.git')
70
- return fs.existsSync(gitDir)
7
+ const options = {
8
+ baseDir: process.cwd(),
9
+ binary: 'git',
10
+ maxConcurrentProcesses: 6,
11
+ trimmed: false,
71
12
  }
72
13
 
73
14
  /**
74
- * Validate a clone source before attempting to clone
15
+ * Configured SimpleGit instance
16
+ * @see https://github.com/steveukx/git-js#api
75
17
  *
76
- * For remote URLs, validation is skipped (let git clone handle network errors).
77
- * For local paths, validates the path exists and is a git repository.
78
- *
79
- * @param {string} source - Clone source (URL or local path)
80
- * @returns {{ valid: boolean, reason?: string }} Validation result
18
+ * @type {SimpleGit}
81
19
  */
82
- export function validateCloneSource(source) {
83
- // Remote URLs are assumed valid - let git clone handle errors
84
- if (isRemoteUrl(source)) {
85
- return { valid: true }
86
- }
87
-
88
- // Local path validation
89
- if (!fs.existsSync(source)) {
90
- return { valid: false, reason: 'path does not exist' }
91
- }
92
-
93
- if (!isGitRepository(source)) {
94
- return { valid: false, reason: 'not a git repository' }
95
- }
96
-
97
- return { valid: true }
98
- }
99
-
100
- /**
101
- * Git façade class
102
- */
103
- class Git {
104
- /**
105
- * Create a Git façade instance
106
- * @param {string} [cwd] - Working directory for all operations
107
- */
108
- constructor(cwd) {
109
- this.cwd = cwd
110
- }
111
-
112
- /**
113
- * Get execa options with working directory
114
- * @private
115
- * @returns {Object} Options object for execa
116
- */
117
- #getOptions() {
118
- return this.cwd ? { cwd: this.cwd } : {}
119
- }
120
-
121
- /**
122
- * Get resolved working directory for logging
123
- * @private
124
- * @returns {string} Resolved absolute path
125
- */
126
- #resolvedCwd() {
127
- return path.resolve(this.cwd || process.cwd())
128
- }
129
-
130
- /**
131
- * Stage files for commit
132
- * @see https://git-scm.com/docs/git-add
133
- * @param {string|string[]} files - Files to stage (use '.' for all)
134
- * @returns {Promise<void>}
135
- */
136
- async add(files) {
137
- const fileList = Array.isArray(files) ? files : [files]
138
- debug('staging files in %s: %s', this.#resolvedCwd(), fileList.join(', '))
139
- const { stderr } = await execa('git', ['add', ...fileList], this.#getOptions())
140
- if (stderr) {
141
- debug('git add stderr: %s', stderr)
142
- }
143
- }
144
-
145
- /**
146
- * Clone a repository
147
- * @see https://git-scm.com/docs/git-clone
148
- * @param {string} url - Repository URL
149
- * @param {string} [destination='.'] - Destination directory
150
- * @returns {Promise<void>}
151
- */
152
- async clone(url, destination = '.') {
153
- const resolvedDest = path.resolve(this.#resolvedCwd(), destination)
154
- debug('cloning %s to %s', url, resolvedDest)
155
- const { stderr } = await execa('git', ['clone', url, destination], this.#getOptions())
156
- if (stderr) {
157
- debug('git clone stderr: %s', stderr)
158
- }
159
- }
160
-
161
- /**
162
- * Create a commit
163
- * @see https://git-scm.com/docs/git-commit
164
- * @param {string} message - Commit message
165
- * @returns {Promise<void>}
166
- */
167
- async commit(message) {
168
- debug('committing in %s: %s...', this.#resolvedCwd(), message.substring(0, 50))
169
- const { stderr } = await execa('git', ['commit', '-m', message], this.#getOptions())
170
- if (stderr) {
171
- debug('git commit stderr: %s', stderr)
172
- }
173
- }
174
-
175
- /**
176
- * Initialize a new repository
177
- * @see https://git-scm.com/docs/git-init
178
- * @returns {Promise<void>}
179
- */
180
- async init() {
181
- debug('initializing repository in %s', this.#resolvedCwd())
182
- const { stderr } = await execa('git', ['init'], this.#getOptions())
183
- if (stderr) {
184
- debug('git init stderr: %s', stderr)
185
- }
186
- }
187
-
188
- /**
189
- * Check if git is available in PATH
190
- * @returns {boolean} True if git is available
191
- */
192
- isAvailable() {
193
- return !!which('git')
194
- }
195
-
196
- /**
197
- * Remove files from the working tree and index
198
- * @see https://git-scm.com/docs/git-rm
199
- * @param {string|string[]} files - Files to remove
200
- * @returns {Promise<void>}
201
- */
202
- async rm(files) {
203
- const fileList = Array.isArray(files) ? files : [files]
204
- debug('removing files in %s: %s', this.#resolvedCwd(), fileList.join(', '))
205
- const { stderr } = await execa('git', ['rm', ...fileList], this.#getOptions())
206
- if (stderr) {
207
- debug('git rm stderr: %s', stderr)
208
- }
209
- }
210
-
211
- /**
212
- * Get git version
213
- * @see https://git-scm.com/docs/git-version
214
- * @returns {Promise<string>} git version string
215
- */
216
- async version() {
217
- const { stdout } = await execa('git', ['--version'])
218
- return stdout.replace('git version ', '')
219
- }
220
- }
221
-
222
- // Export class for repository-scoped operations
223
- export { Git }
20
+ const git = simpleGit(options)
224
21
 
225
- // Export singleton instance for global operations
226
- export default new Git()
22
+ export default git
@@ -0,0 +1,3 @@
1
+ ## CLI Internationalization (i18n) & Localization Module
2
+
3
+ This `quire-cli/lib/i18n` module manages localized message strings for the `quire-cli` and implements a façade for a separate i18n service.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * i18next configuration
3
+ * @see https://www.i18next.com/overview/configuration-options
4
+ *
5
+ * @todo
6
+ * set debug using cli `--debug` option
7
+ * set resources by loading data files
8
+ */
9
+ const configuration = {
10
+ debug: true,
11
+ /**
12
+ * Languages
13
+ */
14
+ fallbackLng: 'en',
15
+ lng: 'en',
16
+ supportedLngs: ['en'],
17
+ /**
18
+ * Namespaces
19
+ */
20
+ defaultNS: 'translation',
21
+ fallbackNS: false,
22
+ ns: 'translation',
23
+ /**
24
+ * Resources
25
+ */
26
+ partialBundledLanguages: false,
27
+ resources: {
28
+ en: {
29
+ translation: {
30
+ 'gradoo': 'blargh',
31
+ }
32
+ },
33
+ },
34
+ /**
35
+ * Missing Keys
36
+ * @ https://www.i18next.com/overview/configuration-options#missing-keys
37
+ */
38
+ saveMissing: false,
39
+ /**
40
+ * Translation defaults
41
+ * @ https://www.i18next.com/overview/configuration-options#translation-defaults
42
+ */
43
+
44
+ /**
45
+ * Plugin options
46
+ * @see https://www.i18next.com/overview/configuration-options#plugin-options
47
+ */
48
+
49
+ /**
50
+ * Other
51
+ * @see https://www.i18next.com/overview/configuration-options#others
52
+ */
53
+ }
@@ -0,0 +1,43 @@
1
+ import { LocaleService } from './LocaleService.js'
2
+ import I18nextCLILanguageDetector from 'i18next-cli-language-detector'
3
+ import i18next from 'i18next'
4
+
5
+ /**
6
+ * i18next configuration
7
+ * @see https://www.i18next.com/overview/configuration-options
8
+ *
9
+ * @todo
10
+ * set debug using cli `--debug` option
11
+ * set resources by loading data files
12
+ */
13
+ const configuration = {
14
+ debug: true,
15
+ /**
16
+ * Languages
17
+ */
18
+ fallbackLng: 'en',
19
+ lng: 'en',
20
+ supportedLngs: ['en'],
21
+ /**
22
+ * Namespaces
23
+ */
24
+ defaultNS: 'translation',
25
+ fallbackNS: false,
26
+ ns: 'translation',
27
+ /**
28
+ * Resources
29
+ */
30
+ resources: {
31
+ en: {
32
+ translation: {
33
+ 'gradoo': 'blargh',
34
+ }
35
+ },
36
+ },
37
+ }
38
+
39
+ await i18next
40
+ .use(I18nextCLILanguageDetector)
41
+ .init(configuration)
42
+
43
+ export default new LocaleService(i18next)
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Localization service
3
+ *
4
+ * @class LocaleService
5
+ */
6
+ export class LocaleService {
7
+ /**
8
+ * @param {Object} i18nProvider
9
+ */
10
+ constructor(i18nProvider) {
11
+ this.i18nProvider = i18nProvider
12
+ }
13
+
14
+ /**
15
+ * Gets the current locale
16
+ *
17
+ * @return {String} current locale code
18
+ */
19
+ getCurrentLocale() {
20
+ return this.i18nProvider.getLocale()
21
+ }
22
+
23
+ /**
24
+ * Get a list of supported locales
25
+ *
26
+ * @return {Array} List of supported locale codes
27
+ */
28
+ getLocales() {
29
+ return this.i18nProvider.getLocales()
30
+ }
31
+
32
+ /**
33
+ * Sets the current locale
34
+ *
35
+ * @param {String} locale i18n locale code
36
+ * @return {String} the current locale code
37
+ */
38
+ setLocale(locale) {
39
+ const locales = this.getLocales()
40
+ if (!locales.includes(locale)) {
41
+ throw new Error(`
42
+ Unknown locale code ${locale} Unable to set current locale;
43
+ supported locales: ${locales}
44
+ `)
45
+ }
46
+ return this.i18nProvider.setLocale(locale)
47
+ }
48
+
49
+ /**
50
+ * Translate a string
51
+ *
52
+ * @param {String} string string to translate
53
+ * @param {Object} options translation options
54
+ */
55
+ translate(string, options) {
56
+ return this.i18nProvider.t(string)
57
+ }
58
+ }
@@ -2,66 +2,6 @@
2
2
 
3
3
  This module provides an abstraction to ease PDF generation across PrinceXML and Paged.js. The exported module dynamically loads a wrapper to align the libraries' JS APIs by exporting a single method that accepts a `lib` option and returns an async function that takes input, output, and option params.
4
4
 
5
- ### Architecture
6
-
7
- ```
8
- ┌─────────────────────────────────────────────────────────────┐
9
- │ lib/pdf/index.js │
10
- │ • Façade: resolves engine, validates prerequisites │
11
- │ • Checks engine availability (fail-fast before reporter) │
12
- │ • Owns reporter lifecycle (start/succeed/fail) │
13
- └─────────────────┬─────────────────────────┬─────────────────┘
14
- │ │
15
- ┌─────────┴─────────┐ ┌─────────┴─────────┐
16
- │ paged.js │ │ prince.js │
17
- │ (Paged.js CLI) │ │ (Prince CLI) │
18
- └─────────┬─────────┘ └─────────┬─────────┘
19
- │ │
20
- └───────────┬─────────────┘
21
-
22
- ┌─────────┴─────────┐
23
- │ split.js │
24
- │ (pdf-lib logic) │
25
- └───────────────────┘
26
- ```
27
-
28
- ### Usage
29
-
30
- ```javascript
31
- import generatePdf from '#lib/pdf/index.js'
32
-
33
- // Basic usage (uses project config for output path)
34
- const pdfPath = await generatePdf({ lib: 'pagedjs' })
35
-
36
- // With custom output path
37
- const pdfPath = await generatePdf({
38
- lib: 'prince',
39
- output: 'downloads/my-book.pdf' // relative to project root
40
- })
41
-
42
- // With absolute path
43
- const pdfPath = await generatePdf({
44
- lib: 'pagedjs',
45
- output: '/tmp/publication.pdf'
46
- })
47
- ```
48
-
49
- ### Options
50
-
51
- | Option | Type | Default | Description |
52
- |--------|------|---------|-------------|
53
- | `lib` | string | `'pagedjs'` | PDF engine: `'pagedjs'` or `'prince'` |
54
- | `output` | string | from config | Output path (overrides project config) |
55
- | `debug` | boolean | `false` | Enable debug output |
56
-
57
- ### Output Path Resolution
58
-
59
- 1. If `options.output` is provided, it is used (relative paths resolved against project root)
60
- 2. Otherwise, uses `config.pdf.outputDir` + `config.pdf.filename` from project config
61
- 3. If no config, falls back to `{projectRoot}/{libName}.pdf`
62
-
63
- ### Page Mapping
64
-
65
5
  The module also provides plugins for Prince and Paged.js to map quire webpages to PDF pages. In both cases this is achieved after PDF rendering by querying the HTML document that was printed for `.quire-page` elements and using the PDF generator's APIs to determine content page ids, page data like titles and contributors, and first / last pages. They then use a simple stripping algorithm with `pdf-lib` to split the pages for `--page-pdf` flagged runs.
66
6
 
67
7
  ### Paged.js Façade
@@ -77,74 +17,3 @@ The Prince abstraction wraps the command line execution of the Prince executable
77
17
  See the [Prince Command-line Reference](https://www.princexml.com/doc/command-line/). Prince's [scripting documentation](https://www.princexml.com/doc/javascript/) has details on its runtime Javascript implementation, *which is only compatible up to ES5*.
78
18
 
79
19
  The Prince plugin passes page map data as JSON to STDOUT.
80
-
81
- ### Test Strategy
82
-
83
- The PDF module uses a layered testing approach:
84
-
85
- | Layer | Test Type | Coverage |
86
- |-------|-----------|----------|
87
- | `index.js` | Unit tests | Engine resolution, path resolution, availability checks |
88
- | `paged.js` | Integration tests | Error paths (Puppeteer failures, file write errors) |
89
- | `prince.js` | Integration tests | Error paths (Prince failures, cancellation handling) |
90
- | `split.js` | Unit tests | Pure PDF manipulation logic with mocked pdf-lib |
91
- | Full flow | E2E tests | Happy paths with real tools |
92
-
93
- #### Why This Strategy?
94
-
95
- **Façade (`index.js`)** - Unit tests verify:
96
- - Engine name normalization (`pagedjs`, `paged`, `paged.js` → same engine)
97
- - Output path resolution priority (CLI > config > fallback)
98
- - Engine availability checking (Prince binary in PATH)
99
- - Error handling for missing prerequisites
100
-
101
- **Engine implementations (`paged.js`, `prince.js`)** - Integration tests for error paths:
102
- - These are orchestration code that wire external tools (Puppeteer, Prince CLI) with reporter updates
103
- - Happy paths are well-covered by E2E tests with real tools
104
- - Error paths (tool crashes, permission errors, cancellation) are hard to trigger in E2E
105
- - Tests mock `execa`/`pagedjs-cli` to verify error wrapping and cleanup
106
-
107
- **PDF splitting (`split.js`)** - Unit tests:
108
- - Pure logic that manipulates PDFs using pdf-lib
109
- - No external tool dependencies
110
- - Tests mock pdf-lib's `PDFDocument` to verify page extraction, cover insertion, and error handling
111
-
112
- **E2E tests** - Full integration:
113
- - Test the complete `quire pdf` command with real publications
114
- - Verify actual PDF output with both engines (when available)
115
- - Cover the happy path that unit/integration tests intentionally skip
116
-
117
- #### Running Tests
118
-
119
- ```bash
120
- # All PDF module tests
121
- npm run test:unit -- src/lib/pdf/
122
-
123
- # Specific test files
124
- npm run test:unit -- src/lib/pdf/index.test.js # façade tests
125
- npm run test:unit -- src/lib/pdf/paged.test.js # Paged.js error paths
126
- npm run test:unit -- src/lib/pdf/prince.test.js # Prince error paths
127
- npm run test:unit -- src/lib/pdf/split.test.js # PDF splitting logic
128
- ```
129
-
130
- ### Error Handling
131
-
132
- All errors are wrapped in typed `PdfGenerationError` with:
133
- - Tool name (`'Prince'`, `'Paged.js'`)
134
- - Operation that failed (`'PDF rendering'`, `'page map extraction'`, etc.)
135
- - Underlying error details
136
-
137
- Example error flow:
138
- ```
139
- Prince CLI fails → execa rejects with stderr
140
- → prince.js catches, wraps in PdfGenerationError
141
- → index.js catches, calls reporter.fail(), re-throws
142
- → Command handler displays formatted error to user
143
- ```
144
-
145
- ### Graceful Shutdown
146
-
147
- Both engines support Ctrl+C cancellation:
148
- - `prince.js` uses `execa`'s `cancelSignal` option
149
- - `paged.js` registers cleanup with `processManager.onShutdown()`
150
- - On cancellation, `reporter.warn('PDF generation cancelled')` notifies the user