@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.
- package/CHANGELOG.md +0 -36
- package/README.md +0 -9
- package/bin/cli.js +1 -46
- package/package.json +9 -25
- package/src/Command.js +6 -26
- package/src/commands/README.md +122 -213
- package/src/commands/build.js +37 -45
- package/src/commands/clean.js +19 -33
- package/src/commands/conf.js +43 -0
- package/src/commands/create.js +14 -42
- package/src/commands/epub.js +31 -67
- package/src/commands/index.js +2 -9
- package/src/commands/info.js +86 -88
- package/src/commands/pdf.js +82 -63
- package/src/commands/preview.js +31 -32
- package/src/commands/validate.js +20 -64
- package/src/commands/version.js +43 -0
- package/src/errors/validation/validation-error.js +11 -20
- package/src/helpers/clean.js +1 -1
- package/src/helpers/is-quire.js +31 -0
- package/src/helpers/test-cwd.js +6 -5
- package/src/helpers/which.js +4 -10
- package/src/lib/11ty/README.md +19 -135
- package/src/lib/11ty/api.js +93 -235
- package/src/lib/11ty/cli.js +37 -112
- package/src/lib/11ty/index.js +5 -64
- package/src/lib/11ty/paths.js +103 -0
- package/src/lib/README.md +0 -330
- package/src/lib/conf/README.md +69 -86
- package/src/lib/conf/config.js +3 -5
- package/src/lib/conf/defaults.js +0 -65
- package/src/lib/conf/schema.js +9 -103
- package/src/lib/epub/README.md +2 -133
- package/src/lib/epub/epub.js +11 -36
- package/src/lib/epub/index.js +21 -111
- package/src/lib/epub/pandoc.js +4 -33
- package/src/lib/git/README.md +2 -151
- package/src/lib/git/index.js +13 -217
- package/src/lib/i18n/README.md +3 -0
- package/src/lib/i18n/config.js +53 -0
- package/src/lib/i18n/index.js +43 -0
- package/src/lib/i18n/localeService.js +58 -0
- package/src/lib/pdf/README.md +0 -131
- package/src/lib/pdf/index.js +21 -124
- package/src/lib/pdf/paged.js +52 -100
- package/src/lib/pdf/prince.js +37 -115
- package/src/lib/pdf/split.js +33 -61
- package/src/lib/quire/README.md +19 -0
- package/src/lib/quire/index.js +353 -0
- package/src/lib/reporter/README.md +2 -211
- package/src/lib/reporter/index.js +0 -607
- package/src/main.js +48 -221
- package/src/packageConfig.js +0 -17
- package/src/validators/utils.js +1 -2
- package/schemas/config.schema.json +0 -194
- package/schemas/figures.schema.json +0 -56
- package/schemas/layout.schema.json +0 -5
- package/schemas/objects.schema.json +0 -62
- package/schemas/publication.schema.json +0 -140
- package/schemas/references.schema.json +0 -29
- package/src/Command.spec.js +0 -99
- package/src/commands/build.spec.js +0 -113
- package/src/commands/build.test.js +0 -397
- package/src/commands/clean.spec.js +0 -118
- package/src/commands/clean.test.js +0 -382
- package/src/commands/config.js +0 -238
- package/src/commands/config.spec.js +0 -107
- package/src/commands/config.test.js +0 -845
- package/src/commands/create.spec.js +0 -112
- package/src/commands/create.test.js +0 -415
- package/src/commands/doctor.js +0 -251
- package/src/commands/doctor.spec.js +0 -115
- package/src/commands/doctor.test.js +0 -1409
- package/src/commands/epub.spec.js +0 -114
- package/src/commands/epub.test.js +0 -503
- package/src/commands/help.js +0 -60
- package/src/commands/help.test.js +0 -132
- package/src/commands/info.spec.js +0 -72
- package/src/commands/info.test.js +0 -512
- package/src/commands/pdf.spec.js +0 -114
- package/src/commands/pdf.test.js +0 -464
- package/src/commands/preview.spec.js +0 -97
- package/src/commands/preview.test.js +0 -250
- package/src/commands/use.js +0 -56
- package/src/commands/use.spec.js +0 -61
- package/src/commands/use.test.js +0 -280
- package/src/commands/validate.spec.js +0 -90
- package/src/commands/validate.test.js +0 -378
- package/src/commands/workflows.js +0 -70
- package/src/errors/build/build-failed-error.js +0 -19
- package/src/errors/build/config-field-missing-error.js +0 -20
- package/src/errors/build/config-file-not-found-error.js +0 -20
- package/src/errors/build/index.js +0 -11
- package/src/errors/help/help-topic-not-found-error.js +0 -30
- package/src/errors/help/index.js +0 -8
- package/src/errors/index.js +0 -58
- package/src/errors/input/index.js +0 -8
- package/src/errors/input/invalid-input-error.js +0 -21
- package/src/errors/install/dependency-install-error.js +0 -19
- package/src/errors/install/directory-not-empty-error.js +0 -25
- package/src/errors/install/index.js +0 -12
- package/src/errors/install/invalid-path-error.js +0 -31
- package/src/errors/install/invalid-starter-error.js +0 -27
- package/src/errors/install/version-not-found-error.js +0 -35
- package/src/errors/output/epub-generation-error.js +0 -19
- package/src/errors/output/index.js +0 -14
- package/src/errors/output/invalid-epub-library-error.js +0 -20
- package/src/errors/output/invalid-pdf-library-error.js +0 -20
- package/src/errors/output/missing-build-output-error.js +0 -21
- package/src/errors/output/pdf-generation-error.js +0 -24
- package/src/errors/output/tool-not-found-error.js +0 -37
- package/src/errors/project/index.js +0 -10
- package/src/errors/project/not-in-project-error.js +0 -20
- package/src/errors/project/project-create-error.js +0 -21
- package/src/errors/quire-error.js +0 -27
- package/src/errors/validation/index.js +0 -3
- package/src/errors/validation/unknown-config-key-error.js +0 -27
- package/src/errors/validation/unknown-config-operation-error.js +0 -24
- package/src/helpers/docs-url.js +0 -32
- package/src/helpers/pager.js +0 -59
- package/src/helpers/pager.test.js +0 -85
- package/src/helpers/suggest-similar.js +0 -124
- package/src/helpers/suggest-similar.test.js +0 -108
- package/src/helpers/test-cwd.test.js +0 -192
- package/src/lib/11ty/index.test.js +0 -655
- package/src/lib/commander/index.js +0 -103
- package/src/lib/commander/index.test.js +0 -86
- package/src/lib/commander/options.js +0 -237
- package/src/lib/commander/options.test.js +0 -205
- package/src/lib/conf/build-status.js +0 -103
- package/src/lib/conf/build-status.test.js +0 -247
- package/src/lib/conf/config.test.js +0 -281
- package/src/lib/conf/format.js +0 -60
- package/src/lib/conf/format.test.js +0 -106
- package/src/lib/conf/helpers.js +0 -91
- package/src/lib/conf/helpers.test.js +0 -136
- package/src/lib/conf/index.js +0 -22
- package/src/lib/constants.js +0 -28
- package/src/lib/doctor/README.md +0 -667
- package/src/lib/doctor/checks/environment/cli-version.js +0 -62
- package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
- package/src/lib/doctor/checks/environment/git-available.js +0 -77
- package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
- package/src/lib/doctor/checks/environment/index.js +0 -13
- package/src/lib/doctor/checks/environment/node-version.js +0 -66
- package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
- package/src/lib/doctor/checks/environment/npm-available.js +0 -66
- package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
- package/src/lib/doctor/checks/environment/os-info.js +0 -48
- package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
- package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
- package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
- package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
- package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
- package/src/lib/doctor/checks/outputs/index.js +0 -10
- package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
- package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
- package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
- package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
- package/src/lib/doctor/checks/project/data-files.js +0 -56
- package/src/lib/doctor/checks/project/data-files.test.js +0 -125
- package/src/lib/doctor/checks/project/dependencies.js +0 -53
- package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
- package/src/lib/doctor/checks/project/index.js +0 -11
- package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
- package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
- package/src/lib/doctor/checks/project/quire-project.js +0 -38
- package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
- package/src/lib/doctor/checks/tools/index.js +0 -10
- package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
- package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
- package/src/lib/doctor/checks/tools/prince-available.js +0 -81
- package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
- package/src/lib/doctor/constants.js +0 -39
- package/src/lib/doctor/formatDuration.js +0 -108
- package/src/lib/doctor/formatDuration.test.js +0 -76
- package/src/lib/doctor/formatters/human.js +0 -257
- package/src/lib/doctor/formatters/human.test.js +0 -463
- package/src/lib/doctor/formatters/index.js +0 -8
- package/src/lib/doctor/formatters/json.js +0 -78
- package/src/lib/doctor/formatters/json.test.js +0 -174
- package/src/lib/doctor/formatters/shared.js +0 -129
- package/src/lib/doctor/formatters/shared.test.js +0 -194
- package/src/lib/doctor/index.js +0 -271
- package/src/lib/doctor/index.test.js +0 -797
- package/src/lib/epub/engines.js +0 -46
- package/src/lib/epub/index.test.js +0 -518
- package/src/lib/epub/pandoc.test.js +0 -122
- package/src/lib/epub/schema.js +0 -21
- package/src/lib/error/README.md +0 -170
- package/src/lib/error/handler.js +0 -107
- package/src/lib/git/index.spec.js +0 -80
- package/src/lib/git/index.test.js +0 -453
- package/src/lib/help/frontmatter.js +0 -77
- package/src/lib/help/frontmatter.test.js +0 -139
- package/src/lib/help/index.js +0 -146
- package/src/lib/help/index.test.js +0 -188
- package/src/lib/help/topics/configuration.md +0 -77
- package/src/lib/help/topics/debugging.md +0 -69
- package/src/lib/help/topics/epub.md +0 -74
- package/src/lib/help/topics/pdf.md +0 -74
- package/src/lib/help/topics/publishing.md +0 -80
- package/src/lib/help/topics/workflows.md +0 -50
- package/src/lib/installer/index.js +0 -309
- package/src/lib/installer/index.spec.js +0 -83
- package/src/lib/installer/index.test.js +0 -545
- package/src/lib/logger/README.md +0 -424
- package/src/lib/logger/debug.js +0 -132
- package/src/lib/logger/debug.spec.js +0 -130
- package/src/lib/logger/debug.test.js +0 -128
- package/src/lib/logger/index.js +0 -228
- package/src/lib/logger/index.spec.js +0 -131
- package/src/lib/logger/index.test.js +0 -578
- package/src/lib/npm/README.md +0 -127
- package/src/lib/npm/index.js +0 -198
- package/src/lib/npm/index.spec.js +0 -60
- package/src/lib/npm/index.test.js +0 -355
- package/src/lib/pdf/engines.js +0 -46
- package/src/lib/pdf/index.test.js +0 -708
- package/src/lib/pdf/paged.test.js +0 -366
- package/src/lib/pdf/prince.test.js +0 -202
- package/src/lib/pdf/schema.js +0 -21
- package/src/lib/pdf/split.test.js +0 -445
- package/src/lib/platform.js +0 -95
- package/src/lib/process/manager.js +0 -110
- package/src/lib/process/manager.test.js +0 -55
- package/src/lib/project/build.js +0 -162
- package/src/lib/project/build.test.js +0 -275
- package/src/lib/project/config.js +0 -48
- package/src/lib/project/config.test.js +0 -134
- package/src/lib/project/detect.js +0 -47
- package/src/lib/project/detect.test.js +0 -157
- package/src/lib/project/index.js +0 -56
- package/src/lib/project/output-paths.js +0 -87
- package/src/lib/project/output-paths.test.js +0 -66
- package/src/lib/project/paths.js +0 -272
- package/src/lib/project/version.js +0 -110
- package/src/lib/project/version.test.js +0 -350
- package/src/lib/reporter/index.test.js +0 -862
- package/src/main.spec.js +0 -61
- package/src/main.test.js +0 -347
- package/src/validators/validate-data-files.js +0 -154
- package/src/validators/validate-data-files.test.js +0 -217
package/src/lib/git/README.md
CHANGED
|
@@ -1,152 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
## CLI Git Module
|
|
2
2
|
|
|
3
|
-
|
|
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`.
|
package/src/lib/git/index.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
7
|
+
const options = {
|
|
8
|
+
baseDir: process.cwd(),
|
|
9
|
+
binary: 'git',
|
|
10
|
+
maxConcurrentProcesses: 6,
|
|
11
|
+
trimmed: false,
|
|
71
12
|
}
|
|
72
13
|
|
|
73
14
|
/**
|
|
74
|
-
*
|
|
15
|
+
* Configured SimpleGit instance
|
|
16
|
+
* @see https://github.com/steveukx/git-js#api
|
|
75
17
|
*
|
|
76
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
export default new Git()
|
|
22
|
+
export default git
|
|
@@ -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
|
+
}
|
package/src/lib/pdf/README.md
CHANGED
|
@@ -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
|