@thegetty/quire-cli 1.0.0-rc.45 → 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 (245) 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 +10 -27
  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/man/quire.1 +0 -341
  56. package/man/quire.1.md +0 -232
  57. package/schemas/config.schema.json +0 -194
  58. package/schemas/figures.schema.json +0 -56
  59. package/schemas/layout.schema.json +0 -5
  60. package/schemas/objects.schema.json +0 -62
  61. package/schemas/publication.schema.json +0 -140
  62. package/schemas/references.schema.json +0 -29
  63. package/src/Command.spec.js +0 -99
  64. package/src/commands/build.spec.js +0 -113
  65. package/src/commands/build.test.js +0 -397
  66. package/src/commands/clean.spec.js +0 -118
  67. package/src/commands/clean.test.js +0 -382
  68. package/src/commands/config.js +0 -238
  69. package/src/commands/config.spec.js +0 -107
  70. package/src/commands/config.test.js +0 -845
  71. package/src/commands/create.spec.js +0 -112
  72. package/src/commands/create.test.js +0 -415
  73. package/src/commands/doctor.js +0 -251
  74. package/src/commands/doctor.spec.js +0 -115
  75. package/src/commands/doctor.test.js +0 -1409
  76. package/src/commands/epub.spec.js +0 -114
  77. package/src/commands/epub.test.js +0 -503
  78. package/src/commands/help.js +0 -60
  79. package/src/commands/help.test.js +0 -132
  80. package/src/commands/info.spec.js +0 -72
  81. package/src/commands/info.test.js +0 -512
  82. package/src/commands/pdf.spec.js +0 -114
  83. package/src/commands/pdf.test.js +0 -464
  84. package/src/commands/preview.spec.js +0 -97
  85. package/src/commands/preview.test.js +0 -250
  86. package/src/commands/use.js +0 -56
  87. package/src/commands/use.spec.js +0 -61
  88. package/src/commands/use.test.js +0 -280
  89. package/src/commands/validate.spec.js +0 -90
  90. package/src/commands/validate.test.js +0 -378
  91. package/src/commands/workflows.js +0 -70
  92. package/src/errors/build/build-failed-error.js +0 -19
  93. package/src/errors/build/config-field-missing-error.js +0 -20
  94. package/src/errors/build/config-file-not-found-error.js +0 -20
  95. package/src/errors/build/index.js +0 -11
  96. package/src/errors/help/help-topic-not-found-error.js +0 -30
  97. package/src/errors/help/index.js +0 -8
  98. package/src/errors/index.js +0 -58
  99. package/src/errors/input/index.js +0 -8
  100. package/src/errors/input/invalid-input-error.js +0 -21
  101. package/src/errors/install/dependency-install-error.js +0 -19
  102. package/src/errors/install/directory-not-empty-error.js +0 -25
  103. package/src/errors/install/index.js +0 -12
  104. package/src/errors/install/invalid-path-error.js +0 -31
  105. package/src/errors/install/invalid-starter-error.js +0 -27
  106. package/src/errors/install/version-not-found-error.js +0 -35
  107. package/src/errors/output/epub-generation-error.js +0 -19
  108. package/src/errors/output/index.js +0 -14
  109. package/src/errors/output/invalid-epub-library-error.js +0 -20
  110. package/src/errors/output/invalid-pdf-library-error.js +0 -20
  111. package/src/errors/output/missing-build-output-error.js +0 -21
  112. package/src/errors/output/pdf-generation-error.js +0 -24
  113. package/src/errors/output/tool-not-found-error.js +0 -37
  114. package/src/errors/project/index.js +0 -10
  115. package/src/errors/project/not-in-project-error.js +0 -20
  116. package/src/errors/project/project-create-error.js +0 -21
  117. package/src/errors/quire-error.js +0 -27
  118. package/src/errors/validation/index.js +0 -3
  119. package/src/errors/validation/unknown-config-key-error.js +0 -27
  120. package/src/errors/validation/unknown-config-operation-error.js +0 -24
  121. package/src/helpers/docs-url.js +0 -32
  122. package/src/helpers/pager.js +0 -59
  123. package/src/helpers/pager.test.js +0 -85
  124. package/src/helpers/suggest-similar.js +0 -124
  125. package/src/helpers/suggest-similar.test.js +0 -108
  126. package/src/helpers/test-cwd.test.js +0 -192
  127. package/src/lib/11ty/index.test.js +0 -655
  128. package/src/lib/commander/index.js +0 -103
  129. package/src/lib/commander/index.test.js +0 -86
  130. package/src/lib/commander/options.js +0 -237
  131. package/src/lib/commander/options.test.js +0 -205
  132. package/src/lib/conf/build-status.js +0 -103
  133. package/src/lib/conf/build-status.test.js +0 -247
  134. package/src/lib/conf/config.test.js +0 -281
  135. package/src/lib/conf/format.js +0 -60
  136. package/src/lib/conf/format.test.js +0 -106
  137. package/src/lib/conf/helpers.js +0 -91
  138. package/src/lib/conf/helpers.test.js +0 -136
  139. package/src/lib/conf/index.js +0 -22
  140. package/src/lib/constants.js +0 -28
  141. package/src/lib/doctor/README.md +0 -667
  142. package/src/lib/doctor/checks/environment/cli-version.js +0 -62
  143. package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
  144. package/src/lib/doctor/checks/environment/git-available.js +0 -77
  145. package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
  146. package/src/lib/doctor/checks/environment/index.js +0 -13
  147. package/src/lib/doctor/checks/environment/node-version.js +0 -66
  148. package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
  149. package/src/lib/doctor/checks/environment/npm-available.js +0 -66
  150. package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
  151. package/src/lib/doctor/checks/environment/os-info.js +0 -48
  152. package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
  153. package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
  154. package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
  155. package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
  156. package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
  157. package/src/lib/doctor/checks/outputs/index.js +0 -10
  158. package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
  159. package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
  160. package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
  161. package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
  162. package/src/lib/doctor/checks/project/data-files.js +0 -56
  163. package/src/lib/doctor/checks/project/data-files.test.js +0 -125
  164. package/src/lib/doctor/checks/project/dependencies.js +0 -53
  165. package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
  166. package/src/lib/doctor/checks/project/index.js +0 -11
  167. package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
  168. package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
  169. package/src/lib/doctor/checks/project/quire-project.js +0 -38
  170. package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
  171. package/src/lib/doctor/checks/tools/index.js +0 -10
  172. package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
  173. package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
  174. package/src/lib/doctor/checks/tools/prince-available.js +0 -81
  175. package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
  176. package/src/lib/doctor/constants.js +0 -39
  177. package/src/lib/doctor/formatDuration.js +0 -108
  178. package/src/lib/doctor/formatDuration.test.js +0 -76
  179. package/src/lib/doctor/formatters/human.js +0 -257
  180. package/src/lib/doctor/formatters/human.test.js +0 -463
  181. package/src/lib/doctor/formatters/index.js +0 -8
  182. package/src/lib/doctor/formatters/json.js +0 -78
  183. package/src/lib/doctor/formatters/json.test.js +0 -174
  184. package/src/lib/doctor/formatters/shared.js +0 -129
  185. package/src/lib/doctor/formatters/shared.test.js +0 -194
  186. package/src/lib/doctor/index.js +0 -271
  187. package/src/lib/doctor/index.test.js +0 -797
  188. package/src/lib/epub/engines.js +0 -46
  189. package/src/lib/epub/index.test.js +0 -518
  190. package/src/lib/epub/pandoc.test.js +0 -122
  191. package/src/lib/epub/schema.js +0 -21
  192. package/src/lib/error/README.md +0 -170
  193. package/src/lib/error/handler.js +0 -107
  194. package/src/lib/git/index.spec.js +0 -80
  195. package/src/lib/git/index.test.js +0 -453
  196. package/src/lib/help/frontmatter.js +0 -77
  197. package/src/lib/help/frontmatter.test.js +0 -139
  198. package/src/lib/help/index.js +0 -146
  199. package/src/lib/help/index.test.js +0 -188
  200. package/src/lib/help/topics/configuration.md +0 -77
  201. package/src/lib/help/topics/debugging.md +0 -69
  202. package/src/lib/help/topics/epub.md +0 -74
  203. package/src/lib/help/topics/pdf.md +0 -74
  204. package/src/lib/help/topics/publishing.md +0 -80
  205. package/src/lib/help/topics/workflows.md +0 -50
  206. package/src/lib/installer/index.js +0 -309
  207. package/src/lib/installer/index.spec.js +0 -83
  208. package/src/lib/installer/index.test.js +0 -545
  209. package/src/lib/logger/README.md +0 -424
  210. package/src/lib/logger/debug.js +0 -132
  211. package/src/lib/logger/debug.spec.js +0 -130
  212. package/src/lib/logger/debug.test.js +0 -128
  213. package/src/lib/logger/index.js +0 -228
  214. package/src/lib/logger/index.spec.js +0 -131
  215. package/src/lib/logger/index.test.js +0 -578
  216. package/src/lib/npm/README.md +0 -127
  217. package/src/lib/npm/index.js +0 -198
  218. package/src/lib/npm/index.spec.js +0 -60
  219. package/src/lib/npm/index.test.js +0 -355
  220. package/src/lib/pdf/engines.js +0 -46
  221. package/src/lib/pdf/index.test.js +0 -708
  222. package/src/lib/pdf/paged.test.js +0 -366
  223. package/src/lib/pdf/prince.test.js +0 -202
  224. package/src/lib/pdf/schema.js +0 -21
  225. package/src/lib/pdf/split.test.js +0 -445
  226. package/src/lib/platform.js +0 -95
  227. package/src/lib/process/manager.js +0 -110
  228. package/src/lib/process/manager.test.js +0 -55
  229. package/src/lib/project/build.js +0 -162
  230. package/src/lib/project/build.test.js +0 -275
  231. package/src/lib/project/config.js +0 -48
  232. package/src/lib/project/config.test.js +0 -134
  233. package/src/lib/project/detect.js +0 -47
  234. package/src/lib/project/detect.test.js +0 -157
  235. package/src/lib/project/index.js +0 -56
  236. package/src/lib/project/output-paths.js +0 -87
  237. package/src/lib/project/output-paths.test.js +0 -66
  238. package/src/lib/project/paths.js +0 -272
  239. package/src/lib/project/version.js +0 -110
  240. package/src/lib/project/version.test.js +0 -350
  241. package/src/lib/reporter/index.test.js +0 -862
  242. package/src/main.spec.js +0 -61
  243. package/src/main.test.js +0 -347
  244. package/src/validators/validate-data-files.js +0 -154
  245. 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