@thegetty/quire-cli 1.0.0-rc.34 → 1.0.0-rc.36
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 +36 -0
- package/README.md +9 -0
- package/bin/cli.js +19 -1
- package/package.json +21 -8
- package/schemas/config.schema.json +194 -0
- package/schemas/figures.schema.json +56 -0
- package/schemas/layout.schema.json +5 -0
- package/schemas/objects.schema.json +62 -0
- package/schemas/publication.schema.json +140 -0
- package/schemas/references.schema.json +29 -0
- package/src/Command.js +26 -6
- package/src/Command.spec.js +99 -0
- package/src/commands/README.md +213 -122
- package/src/commands/build.js +46 -37
- package/src/commands/build.spec.js +113 -0
- package/src/commands/build.test.js +402 -0
- package/src/commands/clean.js +25 -19
- package/src/commands/clean.spec.js +108 -0
- package/src/commands/clean.test.js +260 -0
- package/src/commands/config.js +251 -0
- package/src/commands/config.spec.js +107 -0
- package/src/commands/config.test.js +715 -0
- package/src/commands/create.js +42 -14
- package/src/commands/create.spec.js +112 -0
- package/src/commands/create.test.js +415 -0
- package/src/commands/epub.js +59 -30
- package/src/commands/epub.spec.js +114 -0
- package/src/commands/epub.test.js +503 -0
- package/src/commands/index.js +9 -2
- package/src/commands/info.js +18 -24
- package/src/commands/info.spec.js +64 -0
- package/src/commands/info.test.js +415 -0
- package/src/commands/pdf.js +58 -84
- package/src/commands/pdf.spec.js +114 -0
- package/src/commands/pdf.test.js +464 -0
- package/src/commands/preview.js +26 -31
- package/src/commands/preview.spec.js +97 -0
- package/src/commands/preview.test.js +250 -0
- package/src/commands/use.js +56 -0
- package/src/commands/use.spec.js +61 -0
- package/src/commands/use.test.js +280 -0
- package/src/commands/validate.js +31 -19
- package/src/commands/validate.spec.js +82 -0
- package/src/commands/validate.test.js +234 -0
- package/src/commands/workflows.js +70 -0
- package/src/errors/build/build-failed-error.js +19 -0
- package/src/errors/build/config-field-missing-error.js +20 -0
- package/src/errors/build/config-file-not-found-error.js +20 -0
- package/src/errors/build/index.js +11 -0
- package/src/errors/index.js +48 -0
- package/src/errors/install/dependency-install-error.js +19 -0
- package/src/errors/install/directory-not-empty-error.js +25 -0
- package/src/errors/install/index.js +12 -0
- package/src/errors/install/invalid-path-error.js +31 -0
- package/src/errors/install/invalid-starter-error.js +27 -0
- package/src/errors/install/version-not-found-error.js +35 -0
- package/src/errors/output/epub-generation-error.js +19 -0
- package/src/errors/output/index.js +14 -0
- package/src/errors/output/invalid-epub-library-error.js +20 -0
- package/src/errors/output/invalid-pdf-library-error.js +20 -0
- package/src/errors/output/missing-build-output-error.js +21 -0
- package/src/errors/output/pdf-generation-error.js +24 -0
- package/src/errors/output/tool-not-found-error.js +37 -0
- package/src/errors/project/index.js +10 -0
- package/src/errors/project/not-in-project-error.js +20 -0
- package/src/errors/project/project-create-error.js +21 -0
- package/src/errors/quire-error.js +27 -0
- package/src/errors/validation/validation-error.js +20 -11
- package/src/helpers/clean.js +1 -1
- package/src/helpers/docs-url.js +32 -0
- package/src/helpers/test-cwd.js +5 -6
- package/src/helpers/test-cwd.test.js +192 -0
- package/src/helpers/which.js +10 -4
- package/src/lib/11ty/README.md +135 -19
- package/src/lib/11ty/api.js +176 -93
- package/src/lib/11ty/cli.js +77 -35
- package/src/lib/11ty/index.js +64 -5
- package/src/lib/11ty/index.test.js +655 -0
- package/src/lib/README.md +275 -0
- package/src/lib/commander/index.js +100 -0
- package/src/lib/commander/index.test.js +86 -0
- package/src/lib/commander/options.js +195 -0
- package/src/lib/commander/options.test.js +109 -0
- package/src/lib/conf/README.md +84 -73
- package/src/lib/conf/config.js +5 -3
- package/src/lib/conf/config.test.js +281 -0
- package/src/lib/conf/defaults.js +44 -0
- package/src/lib/conf/format.js +60 -0
- package/src/lib/conf/format.test.js +106 -0
- package/src/lib/conf/helpers.js +91 -0
- package/src/lib/conf/helpers.test.js +136 -0
- package/src/lib/conf/index.js +22 -0
- package/src/lib/conf/schema.js +53 -8
- package/src/lib/epub/README.md +133 -2
- package/src/lib/epub/engines.js +46 -0
- package/src/lib/epub/epub.js +36 -11
- package/src/lib/epub/index.js +111 -21
- package/src/lib/epub/index.test.js +518 -0
- package/src/lib/epub/pandoc.js +33 -4
- package/src/lib/epub/pandoc.test.js +122 -0
- package/src/lib/epub/schema.js +21 -0
- package/src/lib/error/README.md +170 -0
- package/src/lib/error/handler.js +107 -0
- package/src/lib/git/README.md +151 -2
- package/src/lib/git/index.js +217 -13
- package/src/lib/git/index.spec.js +80 -0
- package/src/lib/git/index.test.js +453 -0
- package/src/lib/installer/index.js +309 -0
- package/src/lib/installer/index.spec.js +83 -0
- package/src/lib/installer/index.test.js +545 -0
- package/src/lib/logger/README.md +424 -0
- package/src/lib/logger/debug.js +90 -0
- package/src/lib/logger/debug.spec.js +130 -0
- package/src/lib/logger/index.js +228 -0
- package/src/lib/logger/index.spec.js +131 -0
- package/src/lib/logger/index.test.js +477 -0
- package/src/lib/npm/README.md +127 -0
- package/src/lib/npm/index.js +198 -0
- package/src/lib/npm/index.spec.js +60 -0
- package/src/lib/npm/index.test.js +355 -0
- package/src/lib/pdf/README.md +131 -0
- package/src/lib/pdf/engines.js +46 -0
- package/src/lib/pdf/index.js +124 -21
- package/src/lib/pdf/index.test.js +708 -0
- package/src/lib/pdf/paged.js +100 -52
- package/src/lib/pdf/paged.test.js +366 -0
- package/src/lib/pdf/prince.js +115 -37
- package/src/lib/pdf/prince.test.js +202 -0
- package/src/lib/pdf/schema.js +21 -0
- package/src/lib/pdf/split.js +61 -33
- package/src/lib/pdf/split.test.js +445 -0
- package/src/lib/process/manager.js +110 -0
- package/src/lib/process/manager.test.js +55 -0
- package/src/lib/project/build.js +143 -0
- package/src/lib/project/build.test.js +253 -0
- package/src/lib/project/config.js +48 -0
- package/src/lib/project/config.test.js +134 -0
- package/src/{helpers/is-quire.js → lib/project/detect.js} +5 -3
- package/src/lib/project/detect.test.js +157 -0
- package/src/lib/project/index.js +40 -0
- package/src/lib/project/paths.js +224 -0
- package/src/lib/project/version.js +110 -0
- package/src/lib/project/version.test.js +350 -0
- package/src/lib/reporter/README.md +211 -2
- package/src/lib/reporter/index.js +512 -0
- package/src/lib/reporter/index.test.js +593 -0
- package/src/main.js +134 -47
- package/src/main.spec.js +61 -0
- package/src/main.test.js +347 -0
- package/src/validators/utils.js +2 -1
- package/src/commands/conf.js +0 -43
- package/src/commands/version.js +0 -43
- package/src/lib/11ty/paths.js +0 -103
- package/src/lib/i18n/README.md +0 -3
- package/src/lib/i18n/config.js +0 -53
- package/src/lib/i18n/index.js +0 -43
- package/src/lib/i18n/localeService.js +0 -58
- package/src/lib/quire/README.md +0 -19
- package/src/lib/quire/index.js +0 -350
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { arrayToOption } from './index.js'
|
|
4
|
+
import { quietOption, verboseOption, debugOption } from './options.js'
|
|
5
|
+
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// Integration tests for option conflicts
|
|
8
|
+
//
|
|
9
|
+
// These tests verify that Commander.js correctly enforces the option conflicts
|
|
10
|
+
// at parse time.
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to create a test program with the shared options
|
|
15
|
+
*/
|
|
16
|
+
function createTestProgram() {
|
|
17
|
+
const program = new Command()
|
|
18
|
+
program
|
|
19
|
+
.addOption(arrayToOption(quietOption))
|
|
20
|
+
.addOption(arrayToOption(verboseOption))
|
|
21
|
+
.addOption(arrayToOption(debugOption))
|
|
22
|
+
.action(() => {})
|
|
23
|
+
.exitOverride() // Throw instead of process.exit
|
|
24
|
+
.configureOutput({
|
|
25
|
+
writeErr: () => {}, // Suppress error output
|
|
26
|
+
})
|
|
27
|
+
return program
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test('--quiet alone is valid', (t) => {
|
|
31
|
+
const program = createTestProgram()
|
|
32
|
+
|
|
33
|
+
t.notThrows(() => {
|
|
34
|
+
program.parse(['node', 'test', '--quiet'])
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('--verbose alone is valid', (t) => {
|
|
39
|
+
const program = createTestProgram()
|
|
40
|
+
|
|
41
|
+
t.notThrows(() => {
|
|
42
|
+
program.parse(['node', 'test', '--verbose'])
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('--debug alone is valid', (t) => {
|
|
47
|
+
const program = createTestProgram()
|
|
48
|
+
|
|
49
|
+
t.notThrows(() => {
|
|
50
|
+
program.parse(['node', 'test', '--debug'])
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('--verbose --debug together is valid', (t) => {
|
|
55
|
+
const program = createTestProgram()
|
|
56
|
+
|
|
57
|
+
t.notThrows(() => {
|
|
58
|
+
program.parse(['node', 'test', '--verbose', '--debug'])
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('--quiet --verbose throws conflict error', (t) => {
|
|
63
|
+
const program = createTestProgram()
|
|
64
|
+
|
|
65
|
+
const error = t.throws(() => {
|
|
66
|
+
program.parse(['node', 'test', '--quiet', '--verbose'])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
t.true(error.message.includes('cannot be used with'))
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('--quiet --debug throws conflict error', (t) => {
|
|
73
|
+
const program = createTestProgram()
|
|
74
|
+
|
|
75
|
+
const error = t.throws(() => {
|
|
76
|
+
program.parse(['node', 'test', '--quiet', '--debug'])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
t.true(error.message.includes('cannot be used with'))
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('--quiet --verbose --debug throws conflict error', (t) => {
|
|
83
|
+
const program = createTestProgram()
|
|
84
|
+
|
|
85
|
+
const error = t.throws(() => {
|
|
86
|
+
program.parse(['node', 'test', '--quiet', '--verbose', '--debug'])
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
t.true(error.message.includes('cannot be used with'))
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('-q -v (short flags) throws conflict error', (t) => {
|
|
93
|
+
const program = createTestProgram()
|
|
94
|
+
|
|
95
|
+
const error = t.throws(() => {
|
|
96
|
+
program.parse(['node', 'test', '-q', '-v'])
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
t.true(error.message.includes('cannot be used with'))
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('options order does not affect conflict detection', (t) => {
|
|
103
|
+
const program1 = createTestProgram()
|
|
104
|
+
const program2 = createTestProgram()
|
|
105
|
+
|
|
106
|
+
// Both orders should throw
|
|
107
|
+
t.throws(() => program1.parse(['node', 'test', '--quiet', '--verbose']))
|
|
108
|
+
t.throws(() => program2.parse(['node', 'test', '--verbose', '--quiet']))
|
|
109
|
+
})
|
package/src/lib/conf/README.md
CHANGED
|
@@ -1,104 +1,115 @@
|
|
|
1
|
-
## CLI Configuration
|
|
1
|
+
## CLI Configuration Module
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `conf/` module manages reading and writing (persisting) settings for the Quire CLI using the [`conf`](https://github.com/sindresorhus/conf) package.
|
|
4
4
|
|
|
5
5
|
`conf` stores the config in the system default [user config directory](https://github.com/sindresorhus/env-paths#pathsconfig). For example, on macOS, the config file will be stored in the `~/Library/Preferences/@thegetty/quire-cli` directory.
|
|
6
6
|
|
|
7
7
|
> Changes are written to disk atomically, so if the process crashes during a write, it will not corrupt the existing config.
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Module Structure
|
|
10
10
|
|
|
11
|
-
`logLevel` The default logging level for the Quire CLI output; default `'info'`.
|
|
12
|
-
|
|
13
|
-
`projectTemplate` A default project starter template to use when creating new projects; default `'quire-starter-default'`.
|
|
14
|
-
|
|
15
|
-
`quirePath` The relative path to `quire-11ty` installed in the project directory; default `./11ty`. When set to `null`, `quire-11ty` is installed to the CLI `lib/quire/versions/<version>/` directory.
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
❯ quire config quire-path '.'
|
|
19
11
|
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
conf/
|
|
13
|
+
├── index.js Barrel export (config singleton + helpers + format)
|
|
14
|
+
├── config.js Conf singleton instance
|
|
15
|
+
├── helpers.js Pure schema-aware validation and coercion
|
|
16
|
+
├── format.js Display formatting with optional chalk styling
|
|
17
|
+
├── defaults.js Default configuration values
|
|
18
|
+
├── schema.js JSON Schema definitions
|
|
19
|
+
├── migrations.js Version migration functions
|
|
20
|
+
├── config.test.js Integration tests for Conf instance
|
|
21
|
+
├── helpers.test.js Unit tests for validation/coercion helpers
|
|
22
|
+
└── format.test.js Unit tests for display formatting
|
|
25
23
|
```
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
❯ quire config telemetry --enabled
|
|
31
|
-
```
|
|
25
|
+
### Architecture: Barrel Export vs Singleton Wrapper
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
❯ quire config telemetry --disabled
|
|
35
|
-
```
|
|
27
|
+
The module uses a **barrel export** pattern (`index.js`) rather than wrapping the `Conf` singleton in a class. This is a deliberate design choice:
|
|
36
28
|
|
|
37
|
-
|
|
29
|
+
- **`config.js`** exports the `Conf` singleton that owns mutable state (the on-disk JSON store, file watching, schema validation, migrations). This is a thin wrapper around the `conf` package.
|
|
30
|
+
- **`helpers.js`** exports pure functions (`isValidKey`, `coerceValue`, `formatValidationError`, etc.) for schema-aware validation and coercion. These import only `schema.js` and `defaults.js` (pure data), keeping them free of transitive dependencies.
|
|
31
|
+
- **`format.js`** exports display formatting functions (`formatSettings`, `getKeyDescription`) that depend on `chalk` for optional coloured output. Separated from `helpers.js` to isolate the chalk dependency from the pure validation/coercion functions.
|
|
32
|
+
- **`index.js`** re-exports all three, providing a single import path for consumers.
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
❯ quire config update-channels
|
|
41
|
-
Quire configured to check for updates tagged 'latest'
|
|
42
|
-
```
|
|
34
|
+
A wrapper class was considered but rejected for three reasons:
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```
|
|
36
|
+
1. **No shared state.** The helpers operate on schema/defaults (static data), not the config store (runtime state). Binding them to the singleton would create a false coupling.
|
|
37
|
+
2. **Circular dependency avoidance.** The logger imports `config.js`. If helpers were methods on the config singleton, importing them would trigger the full `Conf` instantiation chain. By depending only on `schema.js` and `defaults.js` (pure data modules), `helpers.js` stays free of transitive dependencies.
|
|
38
|
+
3. **Testability.** Pure functions are directly testable with simple import + assert — no esmock, no sinon, no constructor stubbing. Compare `helpers.test.js` (pure function tests) with `config.test.js` (integration tests requiring esmock to mock the `Conf` constructor).
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
❯ quire config update-channels --rm 'pre-release'
|
|
51
|
-
Quire configured to check for updates tagged 'latest'
|
|
52
|
-
```
|
|
40
|
+
### Exports
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
```javascript
|
|
43
|
+
// Barrel import — config singleton + named helpers
|
|
44
|
+
import config, { isValidKey, coerceValue } from '#lib/conf/index.js'
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Quire configured to check for updates DAILY
|
|
46
|
+
// Direct import — config singleton only (used by logger, etc.)
|
|
47
|
+
import config from '#lib/conf/config.js'
|
|
59
48
|
```
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
| Export | Source | Description |
|
|
51
|
+
|--------|--------|-------------|
|
|
52
|
+
| `default` | `config.js` | `Conf` singleton instance |
|
|
53
|
+
| `isValidKey(key)` | `helpers.js` | Check if key exists in schema |
|
|
54
|
+
| `getValidKeys()` | `helpers.js` | Sorted array of all schema keys |
|
|
55
|
+
| `coerceValue(key, value)` | `helpers.js` | Coerce CLI string to schema type |
|
|
56
|
+
| `formatValidationError(key, value)` | `helpers.js` | Format error with enum/description hints |
|
|
57
|
+
| `getDefault(key)` | `helpers.js` | Get default value for a key |
|
|
58
|
+
| `getKeyDescription(key)` | `format.js` | Get schema description for a key |
|
|
59
|
+
| `formatSettings(store, options)` | `format.js` | Format all settings for display (with optional chalk styling) |
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
### Display Formatting
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
❯ quire config version-file '.blargh'
|
|
69
|
-
```
|
|
63
|
+
`formatSettings` accepts an options object controlling output:
|
|
70
64
|
|
|
71
|
-
|
|
65
|
+
| Option | Type | Default | Description |
|
|
66
|
+
|--------|------|---------|-------------|
|
|
67
|
+
| `configPath` | string | — | Path shown in header (e.g. `quire-cli configuration /path/to/config`) |
|
|
68
|
+
| `showInternal` | boolean | `false` | Include `__internal__`-prefixed keys |
|
|
69
|
+
| `useColor` | boolean | `false` | Apply chalk styling (bold header, cyan keys, dim descriptions) |
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
When `useColor` is `true`, output uses:
|
|
72
|
+
- **Bold** for the header line
|
|
73
|
+
- **Cyan** for key names
|
|
74
|
+
- **Dim** for descriptions and the help hint
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
When `useColor` is `false` (default), no ANSI escape codes are emitted.
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
❯ quire config [key]
|
|
79
|
-
```
|
|
78
|
+
The settings command reads `logUseColor` from the config to determine whether to pass `useColor: true`.
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
### Settings Reference
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
❯ quire config logLevel
|
|
85
|
-
loglevel: 'debug' (default 'info')
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
To set an individual configuration value, use the `set` argument:
|
|
89
|
-
|
|
90
|
-
```sh
|
|
91
|
-
❯ quire config set <key> <value>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
To reset *all* keys to their default values, use the `reset` argument:
|
|
82
|
+
Use `quire settings` to view all settings with descriptions, or manage individual values:
|
|
95
83
|
|
|
96
84
|
```sh
|
|
97
|
-
|
|
85
|
+
quire settings # Show all settings (coloured if logUseColor is true)
|
|
86
|
+
quire settings get <key> # Get a single value
|
|
87
|
+
quire settings set <key> <value> # Set a value
|
|
88
|
+
quire settings delete <key> # Delete (reset to default)
|
|
89
|
+
quire settings reset [key] # Reset all or single key
|
|
90
|
+
quire settings path # Show settings file path
|
|
91
|
+
quire settings --json # Output all settings as JSON
|
|
92
|
+
quire settings get <key> --json # Output single value as JSON
|
|
93
|
+
quire settings --json --debug # Include __internal__ keys in JSON output
|
|
98
94
|
```
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
The `--json` flag outputs raw JSON to stdout (bypassing the logger), suitable for piping to `jq` or other tools. `__internal__` keys are excluded from JSON output unless `--debug` is also passed, consistent with the plain-text display.
|
|
97
|
+
|
|
98
|
+
| Key | Type | Default | Description |
|
|
99
|
+
|-----|------|---------|-------------|
|
|
100
|
+
| `debug` | boolean | `false` | Enable debug output by default |
|
|
101
|
+
| `epubEngine` | string | `'epubjs'` | EPUB engine (`epubjs`, `pandoc`) |
|
|
102
|
+
| `logLevel` | string | `'info'` | Log level (`trace`, `debug`, `info`, `warn`, `error`, `silent`) |
|
|
103
|
+
| `logPrefix` | string | `'quire'` | Prefix text for log messages |
|
|
104
|
+
| `logPrefixStyle` | string | `'bracket'` | Prefix style (`bracket`, `emoji`, `plain`, `none`) |
|
|
105
|
+
| `logShowLevel` | boolean | `false` | Show level label in output |
|
|
106
|
+
| `logUseColor` | boolean | `true` | Use colored output |
|
|
107
|
+
| `logColorMessages` | boolean | `true` | Color message text by level |
|
|
108
|
+
| `pdfEngine` | string | `'pagedjs'` | PDF engine (`pagedjs`, `prince`) |
|
|
109
|
+
| `projectTemplate` | string | (GitHub URL) | Default starter template |
|
|
110
|
+
| `quire11tyPath` | string | `'.'` | Path to quire-11ty package |
|
|
111
|
+
| `quireVersion` | string | `'latest'` | Version of quire-11ty to install |
|
|
112
|
+
| `updateChannel` | string | `'rc'` | Release channel (`stable`, `rc`, `beta`, `alpha`) |
|
|
113
|
+
| `updateInterval` | string | `'DAILY'` | Update check frequency (`DAILY`, `WEEKLY`, `MONTHLY`, `NEVER`) |
|
|
114
|
+
| `verbose` | boolean | `false` | Enable verbose output by default |
|
|
115
|
+
| `versionFile` | string | `'.quire'` | Filename to identify Quire projects |
|
package/src/lib/conf/config.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import Conf from 'conf'
|
|
2
|
+
import packageConfig from '#src/packageConfig.js'
|
|
2
3
|
import defaults from './defaults.js'
|
|
3
4
|
import migrations from './migrations.js'
|
|
4
|
-
import packageConfig from '#src/packageConfig.js'
|
|
5
5
|
import schema from './schema.js'
|
|
6
6
|
|
|
7
7
|
const { name, version } = packageConfig
|
|
8
8
|
|
|
9
|
-
const beforeEachMigration = (
|
|
9
|
+
const beforeEachMigration = (_store, context) => {
|
|
10
10
|
const { fromVersion, toVersion } = context
|
|
11
|
-
console
|
|
11
|
+
// Call console directly to avoid circular dependency (logger imports config)
|
|
12
|
+
// Migration messages are rare, so the simpler formatting is acceptable
|
|
13
|
+
console.info(`[quire] Migrating config from ${fromVersion} → ${toVersion}`)
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import esmock from 'esmock'
|
|
2
|
+
import sinon from 'sinon'
|
|
3
|
+
import test from 'ava'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration Module Integration Tests
|
|
7
|
+
*
|
|
8
|
+
* Tests the Quire CLI configuration module behavior with mocked dependencies.
|
|
9
|
+
* Uses esmock to isolate the module from the actual Conf library and filesystem.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test.beforeEach((t) => {
|
|
13
|
+
t.context.sandbox = sinon.createSandbox()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test.afterEach.always((t) => {
|
|
17
|
+
t.context.sandbox.restore()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Configuration instantiation tests
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
test('config is created with correct project name and version', async (t) => {
|
|
25
|
+
const { sandbox } = t.context
|
|
26
|
+
|
|
27
|
+
let capturedOptions = null
|
|
28
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
29
|
+
capturedOptions = options
|
|
30
|
+
return {
|
|
31
|
+
get: sandbox.stub(),
|
|
32
|
+
set: sandbox.stub()
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
await esmock('./config.js', {
|
|
37
|
+
conf: { default: MockConf },
|
|
38
|
+
'#src/packageConfig.js': { name: '@thegetty/quire-cli', version: '1.0.0' }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
t.true(MockConf.calledOnce)
|
|
42
|
+
t.is(capturedOptions.projectName, '@thegetty/quire-cli')
|
|
43
|
+
t.is(capturedOptions.projectVersion, '1.0.0')
|
|
44
|
+
t.is(capturedOptions.projectSuffix, '')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('config is created with correct options', async (t) => {
|
|
48
|
+
const { sandbox } = t.context
|
|
49
|
+
|
|
50
|
+
let capturedOptions = null
|
|
51
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
52
|
+
capturedOptions = options
|
|
53
|
+
return {
|
|
54
|
+
get: sandbox.stub(),
|
|
55
|
+
set: sandbox.stub()
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await esmock('./config.js', {
|
|
60
|
+
conf: { default: MockConf },
|
|
61
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
t.true(capturedOptions.clearInvalidConfig)
|
|
65
|
+
t.true(capturedOptions.watch)
|
|
66
|
+
t.is(typeof capturedOptions.beforeEachMigration, 'function')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('config passes defaults to Conf constructor', async (t) => {
|
|
70
|
+
const { sandbox } = t.context
|
|
71
|
+
|
|
72
|
+
let capturedOptions = null
|
|
73
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
74
|
+
capturedOptions = options
|
|
75
|
+
return {
|
|
76
|
+
get: sandbox.stub(),
|
|
77
|
+
set: sandbox.stub()
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const mockDefaults = {
|
|
82
|
+
logLevel: 'debug',
|
|
83
|
+
quireVersion: '2.0.0'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await esmock('./config.js', {
|
|
87
|
+
conf: { default: MockConf },
|
|
88
|
+
'./defaults.js': { default: mockDefaults },
|
|
89
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
t.deepEqual(capturedOptions.defaults, mockDefaults)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('config passes schema to Conf constructor', async (t) => {
|
|
96
|
+
const { sandbox } = t.context
|
|
97
|
+
|
|
98
|
+
let capturedOptions = null
|
|
99
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
100
|
+
capturedOptions = options
|
|
101
|
+
return {
|
|
102
|
+
get: sandbox.stub(),
|
|
103
|
+
set: sandbox.stub()
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const mockSchema = {
|
|
108
|
+
logLevel: { type: 'string' },
|
|
109
|
+
quireVersion: { type: 'string' }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await esmock('./config.js', {
|
|
113
|
+
conf: { default: MockConf },
|
|
114
|
+
'./schema.js': { default: mockSchema },
|
|
115
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
t.deepEqual(capturedOptions.schema, mockSchema)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('config passes migrations to Conf constructor', async (t) => {
|
|
122
|
+
const { sandbox } = t.context
|
|
123
|
+
|
|
124
|
+
let capturedOptions = null
|
|
125
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
126
|
+
capturedOptions = options
|
|
127
|
+
return {
|
|
128
|
+
get: sandbox.stub(),
|
|
129
|
+
set: sandbox.stub()
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
const mockMigrations = {
|
|
134
|
+
'1.0.0': () => {},
|
|
135
|
+
'2.0.0': () => {}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await esmock('./config.js', {
|
|
139
|
+
conf: { default: MockConf },
|
|
140
|
+
'./migrations.js': { default: mockMigrations },
|
|
141
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
t.deepEqual(capturedOptions.migrations, mockMigrations)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
// Migration callback tests
|
|
149
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
test.serial('beforeEachMigration logs migration info', async (t) => {
|
|
152
|
+
const { sandbox } = t.context
|
|
153
|
+
|
|
154
|
+
let capturedOptions = null
|
|
155
|
+
const MockConf = sandbox.stub().callsFake(function (options) {
|
|
156
|
+
capturedOptions = options
|
|
157
|
+
return {
|
|
158
|
+
get: sandbox.stub(),
|
|
159
|
+
set: sandbox.stub()
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const consoleInfoStub = sandbox.stub(console, 'info')
|
|
164
|
+
|
|
165
|
+
await esmock('./config.js', {
|
|
166
|
+
conf: { default: MockConf },
|
|
167
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Call the beforeEachMigration callback
|
|
171
|
+
const mockStore = { get: sandbox.stub(), set: sandbox.stub() }
|
|
172
|
+
const mockContext = { fromVersion: '0.9.0', toVersion: '1.0.0' }
|
|
173
|
+
capturedOptions.beforeEachMigration(mockStore, mockContext)
|
|
174
|
+
|
|
175
|
+
t.true(consoleInfoStub.calledOnce)
|
|
176
|
+
t.true(consoleInfoStub.firstCall.args[0].includes('0.9.0'))
|
|
177
|
+
t.true(consoleInfoStub.firstCall.args[0].includes('1.0.0'))
|
|
178
|
+
t.true(consoleInfoStub.firstCall.args[0].includes('Migrating'))
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
182
|
+
// Module export tests
|
|
183
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
test('config module exports Conf instance as default', async (t) => {
|
|
186
|
+
const { sandbox } = t.context
|
|
187
|
+
|
|
188
|
+
const mockInstance = {
|
|
189
|
+
get: sandbox.stub().returns('value'),
|
|
190
|
+
set: sandbox.stub(),
|
|
191
|
+
has: sandbox.stub().returns(true),
|
|
192
|
+
delete: sandbox.stub()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const MockConf = sandbox.stub().returns(mockInstance)
|
|
196
|
+
|
|
197
|
+
const config = await esmock('./config.js', {
|
|
198
|
+
conf: { default: MockConf },
|
|
199
|
+
'#src/packageConfig.js': { name: 'test', version: '1.0.0' }
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
t.is(config.default, mockInstance)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
206
|
+
// Default values tests
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
test('defaults module exports expected configuration keys', async (t) => {
|
|
210
|
+
const defaults = await import('./defaults.js')
|
|
211
|
+
|
|
212
|
+
t.true('logLevel' in defaults.default)
|
|
213
|
+
t.true('projectTemplate' in defaults.default)
|
|
214
|
+
t.true('quire11tyPath' in defaults.default)
|
|
215
|
+
t.true('quireVersion' in defaults.default)
|
|
216
|
+
t.true('updateChannel' in defaults.default)
|
|
217
|
+
t.true('updateInterval' in defaults.default)
|
|
218
|
+
t.true('versionFile' in defaults.default)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test('defaults module has reasonable default values', async (t) => {
|
|
222
|
+
const defaults = await import('./defaults.js')
|
|
223
|
+
|
|
224
|
+
t.is(defaults.default.logLevel, 'info')
|
|
225
|
+
t.is(defaults.default.quireVersion, 'latest')
|
|
226
|
+
t.is(defaults.default.versionFile, '.quire')
|
|
227
|
+
t.true(defaults.default.projectTemplate.includes('github.com'))
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
231
|
+
// Schema tests
|
|
232
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
test('schema module defines types for all default keys', async (t) => {
|
|
235
|
+
const schema = await import('./schema.js')
|
|
236
|
+
const defaults = await import('./defaults.js')
|
|
237
|
+
|
|
238
|
+
const schemaKeys = Object.keys(schema.default)
|
|
239
|
+
const defaultKeys = Object.keys(defaults.default)
|
|
240
|
+
|
|
241
|
+
// All default keys should have schema definitions
|
|
242
|
+
for (const key of defaultKeys) {
|
|
243
|
+
t.true(schemaKeys.includes(key), `Schema should define ${key}`)
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test('schema module defines valid types for all properties', async (t) => {
|
|
248
|
+
const schema = await import('./schema.js')
|
|
249
|
+
|
|
250
|
+
const validTypes = ['string', 'boolean', 'number', 'object', 'array']
|
|
251
|
+
for (const [key, value] of Object.entries(schema.default)) {
|
|
252
|
+
t.true(validTypes.includes(value.type), `${key} should have a valid type`)
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
257
|
+
// Migrations tests
|
|
258
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
test('migrations module exports migration functions', async (t) => {
|
|
261
|
+
const migrations = await import('./migrations.js')
|
|
262
|
+
|
|
263
|
+
t.is(typeof migrations.default, 'object')
|
|
264
|
+
t.true('1.0.0' in migrations.default)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
test('migrations 1.0.0 sets updateChannel', async (t) => {
|
|
268
|
+
const { sandbox } = t.context
|
|
269
|
+
|
|
270
|
+
const migrations = await import('./migrations.js')
|
|
271
|
+
|
|
272
|
+
const mockStore = {
|
|
273
|
+
get: sandbox.stub(),
|
|
274
|
+
set: sandbox.stub()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
migrations.default['1.0.0'](mockStore)
|
|
278
|
+
|
|
279
|
+
t.true(mockStore.set.calledOnce)
|
|
280
|
+
t.true(mockStore.set.calledWith('updateChannel', 'latest'))
|
|
281
|
+
})
|
package/src/lib/conf/defaults.js
CHANGED
|
@@ -5,10 +5,49 @@
|
|
|
5
5
|
* @see https://github.com/sindresorhus/conf#defaults
|
|
6
6
|
*/
|
|
7
7
|
export default {
|
|
8
|
+
/**
|
|
9
|
+
* Enable debug output by default (DEBUG=quire:* namespace logging).
|
|
10
|
+
* Can be overridden per-command with --debug or --no-debug.
|
|
11
|
+
*/
|
|
12
|
+
debug: false,
|
|
13
|
+
/**
|
|
14
|
+
* Default EPUB engine for quire epub command.
|
|
15
|
+
*/
|
|
16
|
+
epubEngine: 'epubjs',
|
|
8
17
|
/**
|
|
9
18
|
* Logging level for the Quire CLI output.
|
|
10
19
|
*/
|
|
11
20
|
logLevel: 'info',
|
|
21
|
+
/**
|
|
22
|
+
* Prefix text for log output messages.
|
|
23
|
+
*/
|
|
24
|
+
logPrefix: 'quire',
|
|
25
|
+
/**
|
|
26
|
+
* Style of the log prefix.
|
|
27
|
+
* - 'bracket': [quire] message
|
|
28
|
+
* - 'emoji': 📖 message
|
|
29
|
+
* - 'plain': quire: message
|
|
30
|
+
* - 'none': message (no prefix)
|
|
31
|
+
*/
|
|
32
|
+
logPrefixStyle: 'bracket',
|
|
33
|
+
/**
|
|
34
|
+
* Whether to show log level labels (INFO, WARN, ERROR).
|
|
35
|
+
* Defaults to false for cleaner user output.
|
|
36
|
+
*/
|
|
37
|
+
logShowLevel: false,
|
|
38
|
+
/**
|
|
39
|
+
* Whether to use colored output in terminal.
|
|
40
|
+
*/
|
|
41
|
+
logUseColor: true,
|
|
42
|
+
/**
|
|
43
|
+
* Whether to color message text by log level (e.g., red for errors).
|
|
44
|
+
* Requires logUseColor to be enabled.
|
|
45
|
+
*/
|
|
46
|
+
logColorMessages: true,
|
|
47
|
+
/**
|
|
48
|
+
* Default PDF engine for quire pdf command.
|
|
49
|
+
*/
|
|
50
|
+
pdfEngine: 'pagedjs',
|
|
12
51
|
/**
|
|
13
52
|
* Project starter template to use when creating new projects.
|
|
14
53
|
*/
|
|
@@ -30,6 +69,11 @@ export default {
|
|
|
30
69
|
* and to quire-11ty version for a prject.
|
|
31
70
|
*/
|
|
32
71
|
updateInterval: 'DAILY',
|
|
72
|
+
/**
|
|
73
|
+
* Enable verbose output by default (detailed progress with paths, timing).
|
|
74
|
+
* Can be overridden per-command with --verbose or --no-verbose.
|
|
75
|
+
*/
|
|
76
|
+
verbose: false,
|
|
33
77
|
/**
|
|
34
78
|
* File name for the quire-11ty version file.
|
|
35
79
|
*/
|