@thegetty/quire-cli 1.0.0-rc.35 → 1.0.0-rc.37
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/patches/README.md +19 -0
- package/patches/install-npm-version+1.0.9.patch +12119 -0
- package/schemas/objects.schema.json +12 -38
- package/schemas/publication.schema.json +0 -22
- package/schemas/references.schema.json +0 -1
- 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 +37 -36
- package/src/commands/build.spec.js +113 -0
- package/src/commands/build.test.js +397 -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 +32 -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 +196 -94
- package/src/lib/11ty/cli.js +91 -37
- 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 +132 -0
- package/src/lib/logger/debug.spec.js +130 -0
- package/src/lib/logger/debug.test.js +128 -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,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper module for constructing documentation URLs
|
|
3
|
+
* @module helpers/docs-url
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base URL for Quire documentation
|
|
8
|
+
*/
|
|
9
|
+
const DOCS_BASE = 'https://quire.getty.edu/docs-v1/'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Export the base URL for cases where direct access is needed
|
|
13
|
+
*/
|
|
14
|
+
export { DOCS_BASE }
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Construct a full documentation URL from a path segment
|
|
18
|
+
*
|
|
19
|
+
* Uses the URL constructor for automatic path normalization.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} path - Documentation path segment (e.g., 'install-uninstall')
|
|
22
|
+
* @returns {string} Full documentation URL
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* docsUrl('install-uninstall')
|
|
26
|
+
* // => 'https://quire.getty.edu/docs-v1/install-uninstall'
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* docsUrl('quire-commands')
|
|
30
|
+
* // => 'https://quire.getty.edu/docs-v1/quire-commands'
|
|
31
|
+
*/
|
|
32
|
+
export const docsUrl = (path) => new URL(path, DOCS_BASE).href
|
package/src/helpers/test-cwd.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A helper module to test the current working directory
|
|
3
|
-
* @module test-
|
|
3
|
+
* @module test-cwd
|
|
4
4
|
*/
|
|
5
|
-
import isQuire from '#
|
|
5
|
+
import { detect as isQuire } from '#lib/project/index.js'
|
|
6
|
+
import { NotInProjectError } from '#src/errors/index.js'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Test current working directory is a Quire project directory
|
|
9
10
|
*
|
|
10
11
|
* @param {Command} command the command from which testcwd was called
|
|
12
|
+
* @throws {NotInProjectError} if not in a Quire project directory
|
|
11
13
|
*/
|
|
12
14
|
export default (command) => {
|
|
13
|
-
const message = `[CLI] ${ command ? command.name() : '' } command must be run while in a Quire project directory. Use 'cd' to navigate to your project directory and re-run the 'quire ${ command ? command.name() : '' }' command.`
|
|
14
|
-
|
|
15
15
|
if (!isQuire(process.cwd())) {
|
|
16
|
-
|
|
17
|
-
process.exit(1)
|
|
16
|
+
throw new NotInProjectError(command?.name() || '')
|
|
18
17
|
}
|
|
19
18
|
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
import { Volume, createFsFromVolume } from 'memfs'
|
|
3
|
+
import sinon from 'sinon'
|
|
4
|
+
import esmock from 'esmock'
|
|
5
|
+
import { NotInProjectError } from '#src/errors/index.js'
|
|
6
|
+
|
|
7
|
+
test.beforeEach((t) => {
|
|
8
|
+
// Create in-memory file system
|
|
9
|
+
t.context.vol = new Volume()
|
|
10
|
+
t.context.fs = createFsFromVolume(t.context.vol)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test.afterEach.always((t) => {
|
|
14
|
+
// Restore all mocks if sandbox exists
|
|
15
|
+
if (t.context.sandbox) {
|
|
16
|
+
t.context.sandbox.restore()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Clear in-memory file system
|
|
20
|
+
t.context.vol.reset()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test.serial('testcwd should not error when called in a Quire project directory', async (t) => {
|
|
24
|
+
const { fs, vol } = t.context
|
|
25
|
+
|
|
26
|
+
// Create sandbox for this test
|
|
27
|
+
const sandbox = sinon.createSandbox()
|
|
28
|
+
t.context.sandbox = sandbox
|
|
29
|
+
|
|
30
|
+
// Setup a Quire project directory with .eleventy.js
|
|
31
|
+
vol.fromJSON({
|
|
32
|
+
'/quire-project/.eleventy.js': 'module.exports = function() {}',
|
|
33
|
+
'/quire-project/content/_data/config.yaml': 'title: Test Project',
|
|
34
|
+
'/quire-project/package.json': JSON.stringify({ name: 'test-project' })
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Create stubs
|
|
38
|
+
const cwdStub = sandbox.stub(process, 'cwd').returns('/quire-project')
|
|
39
|
+
|
|
40
|
+
// Create mock process object with stubbed methods
|
|
41
|
+
const mockProcess = {
|
|
42
|
+
cwd: cwdStub
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Mock detect module with memfs
|
|
46
|
+
const mockDetect = await esmock('../lib/project/detect.js', {
|
|
47
|
+
'node:fs': fs
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Mock node:fs and node:process to use memfs and stubbed process
|
|
51
|
+
const testcwd = await esmock('./test-cwd.js', {
|
|
52
|
+
'node:process': mockProcess,
|
|
53
|
+
'#lib/project/index.js': {
|
|
54
|
+
detect: mockDetect
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Create mock command
|
|
59
|
+
const mockCommand = {
|
|
60
|
+
name: sandbox.stub().returns('build')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Call testcwd - should not throw
|
|
64
|
+
t.notThrows(() => testcwd(mockCommand))
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test.serial('testcwd should throw NotInProjectError when called outside a Quire project directory', async (t) => {
|
|
68
|
+
const { fs, vol } = t.context
|
|
69
|
+
|
|
70
|
+
// Create sandbox for this test
|
|
71
|
+
const sandbox = sinon.createSandbox()
|
|
72
|
+
t.context.sandbox = sandbox
|
|
73
|
+
|
|
74
|
+
// Setup a non-Quire directory (no .eleventy.js or other Quire marker files)
|
|
75
|
+
vol.fromJSON({
|
|
76
|
+
'/not-quire/some-file.txt': 'not a quire project',
|
|
77
|
+
'/not-quire/package.json': JSON.stringify({ name: 'not-a-quire-project' })
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Create stubs
|
|
81
|
+
const cwdStub = sandbox.stub(process, 'cwd').returns('/not-quire')
|
|
82
|
+
|
|
83
|
+
// Create mock process object with stubbed methods
|
|
84
|
+
const mockProcess = {
|
|
85
|
+
cwd: cwdStub
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Mock detect module with memfs
|
|
89
|
+
const mockDetect = await esmock('../lib/project/detect.js', {
|
|
90
|
+
'node:fs': fs
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Mock node:fs and node:process to use memfs and stubbed process
|
|
94
|
+
const testcwd = await esmock('./test-cwd.js', {
|
|
95
|
+
'node:process': mockProcess,
|
|
96
|
+
'#lib/project/index.js': {
|
|
97
|
+
detect: mockDetect
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Create mock command
|
|
102
|
+
const mockCommand = {
|
|
103
|
+
name: sandbox.stub().returns('build')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Call testcwd - should throw NotInProjectError
|
|
107
|
+
const error = t.throws(() => testcwd(mockCommand), { instanceOf: NotInProjectError })
|
|
108
|
+
t.is(error.code, 'NOT_IN_PROJECT')
|
|
109
|
+
t.is(error.exitCode, 2)
|
|
110
|
+
t.true(error.message.includes('build'))
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test.serial('testcwd should include command name in error message', async (t) => {
|
|
114
|
+
const { fs, vol } = t.context
|
|
115
|
+
|
|
116
|
+
// Create sandbox for this test
|
|
117
|
+
const sandbox = sinon.createSandbox()
|
|
118
|
+
t.context.sandbox = sandbox
|
|
119
|
+
|
|
120
|
+
// Setup a non-Quire directory
|
|
121
|
+
vol.fromJSON({
|
|
122
|
+
'/non-quire-directory/file.txt': 'test'
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Create stubs
|
|
126
|
+
const cwdStub = sandbox.stub(process, 'cwd').returns('/non-quire-directory')
|
|
127
|
+
|
|
128
|
+
// Create mock process object with stubbed methods
|
|
129
|
+
const mockProcess = {
|
|
130
|
+
cwd: cwdStub
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Mock detect module with memfs
|
|
134
|
+
const mockDetect = await esmock('../lib/project/detect.js', {
|
|
135
|
+
'node:fs': fs
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Mock node:fs and node:process to use memfs and stubbed process
|
|
139
|
+
const testcwd = await esmock('./test-cwd.js', {
|
|
140
|
+
'node:process': mockProcess,
|
|
141
|
+
'#lib/project/index.js': {
|
|
142
|
+
detect: mockDetect
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Create mock command with specific name
|
|
147
|
+
const mockCommand = {
|
|
148
|
+
name: sandbox.stub().returns('clean')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Call testcwd - should throw with command name in message
|
|
152
|
+
const error = t.throws(() => testcwd(mockCommand), { instanceOf: NotInProjectError })
|
|
153
|
+
t.true(error.message.includes('clean'), 'error message should include command name')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test.serial('testcwd should work when command is null', async (t) => {
|
|
157
|
+
const { fs, vol } = t.context
|
|
158
|
+
|
|
159
|
+
// Create sandbox for this test
|
|
160
|
+
const sandbox = sinon.createSandbox()
|
|
161
|
+
t.context.sandbox = sandbox
|
|
162
|
+
|
|
163
|
+
// Setup a non-Quire directory
|
|
164
|
+
vol.fromJSON({
|
|
165
|
+
'/not-a-quire-directory/file.txt': 'test'
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// Create stubs
|
|
169
|
+
const cwdStub = sandbox.stub(process, 'cwd').returns('/not-a-quire-directory')
|
|
170
|
+
|
|
171
|
+
// Create mock process object with stubbed methods
|
|
172
|
+
const mockProcess = {
|
|
173
|
+
cwd: cwdStub
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Mock detect module with memfs
|
|
177
|
+
const mockDetect = await esmock('../lib/project/detect.js', {
|
|
178
|
+
'node:fs': fs
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Mock node:fs and node:process to use memfs and stubbed process
|
|
182
|
+
const testcwd = await esmock('./test-cwd.js', {
|
|
183
|
+
'node:process': mockProcess,
|
|
184
|
+
'#lib/project/index.js': {
|
|
185
|
+
detect: mockDetect
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// Call testcwd with null command - should throw but not crash
|
|
190
|
+
const error = t.throws(() => testcwd(null), { instanceOf: NotInProjectError })
|
|
191
|
+
t.is(error.exitCode, 2)
|
|
192
|
+
})
|
package/src/helpers/which.js
CHANGED
|
@@ -3,19 +3,25 @@
|
|
|
3
3
|
* @module which
|
|
4
4
|
*/
|
|
5
5
|
import which from 'which'
|
|
6
|
+
import { ToolNotFoundError } from '#src/errors/index.js'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A simple wrapper around the `which` utility
|
|
9
10
|
* to find the first instance of an executable in the shell environment PATH
|
|
10
11
|
*
|
|
11
12
|
* @param {String} executable shell executable to find
|
|
13
|
+
* @param {Object} [toolInfo] optional metadata for error messages
|
|
14
|
+
* @param {String} [toolInfo.displayName] human-readable tool name
|
|
15
|
+
* @param {String} [toolInfo.installUrl] URL where tool can be downloaded
|
|
16
|
+
* @param {String} [toolInfo.docsUrl] Quire docs URL for this tool
|
|
17
|
+
* @param {String} [toolInfo.fallback] alternative suggestion
|
|
18
|
+
* @throws {ToolNotFoundError} When executable is not found in PATH
|
|
19
|
+
* @returns {String} Path to the executable
|
|
12
20
|
*/
|
|
13
|
-
export default (executable) => {
|
|
21
|
+
export default (executable, toolInfo) => {
|
|
14
22
|
const result = which.sync(executable, { nothrow: true })
|
|
15
23
|
if (!result) {
|
|
16
|
-
|
|
17
|
-
Ensure that the '${executable}' executable is installed and available in the shell environment PATH.
|
|
18
|
-
`)
|
|
24
|
+
throw new ToolNotFoundError(executable, toolInfo)
|
|
19
25
|
}
|
|
20
26
|
return result
|
|
21
27
|
}
|
package/src/lib/11ty/README.md
CHANGED
|
@@ -1,35 +1,151 @@
|
|
|
1
1
|
## CLI 11ty Module
|
|
2
2
|
|
|
3
|
-
The `
|
|
3
|
+
The `lib/11ty` module is a façade for interacting with Eleventy, the static site generator for Quire projects. It follows the singleton pattern established by `lib/npm` and `lib/git`.
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Usage
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Recommended: Default export**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```javascript
|
|
10
|
+
import eleventy from '#lib/11ty/index.js'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Run production build
|
|
13
|
+
await eleventy.build({ debug: true })
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
// Run development server
|
|
16
|
+
await eleventy.serve({ port: 8080 })
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
// Access paths through the façade
|
|
19
|
+
const outputDir = eleventy.paths.getOutputDir()
|
|
20
|
+
```
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
**Path-only consumers**
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
```javascript
|
|
25
|
+
import { paths } from '#lib/11ty/index.js'
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
const projectRoot = paths.getProjectRoot()
|
|
28
|
+
const outputDir = paths.getOutputDir()
|
|
29
|
+
```
|
|
24
30
|
|
|
31
|
+
**Legacy usage (deprecated)**
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
```javascript
|
|
34
|
+
// Deprecated - use default export instead
|
|
35
|
+
import { api, cli, paths } from '#lib/11ty/index.js'
|
|
36
|
+
await api.build(options)
|
|
37
|
+
```
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
### Test Mocking
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
```javascript
|
|
42
|
+
const mockEleventy = {
|
|
43
|
+
build: sandbox.stub().resolves(),
|
|
44
|
+
serve: sandbox.stub().resolves(),
|
|
45
|
+
paths: {
|
|
46
|
+
getProjectRoot: () => '/project',
|
|
47
|
+
getOutputDir: () => '_site'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const MyCommand = await esmock('./mycommand.js', {
|
|
51
|
+
'#lib/11ty/index.js': { default: mockEleventy }
|
|
52
|
+
})
|
|
53
|
+
```
|
|
34
54
|
|
|
35
|
-
|
|
55
|
+
### Quire11ty Façade
|
|
56
|
+
|
|
57
|
+
The `Quire11ty` class provides abstracted Eleventy operations:
|
|
58
|
+
|
|
59
|
+
| Method | Description |
|
|
60
|
+
|--------|-------------|
|
|
61
|
+
| `build(options)` | Run Eleventy production build |
|
|
62
|
+
| `serve(options)` | Run Eleventy development server |
|
|
63
|
+
| `paths` | Access to `Paths` instance for path resolution |
|
|
64
|
+
|
|
65
|
+
**Build options:**
|
|
66
|
+
- `debug` - Enable debug output
|
|
67
|
+
- `dryRun` - Perform dry run without writing files
|
|
68
|
+
- `quiet` - Suppress output
|
|
69
|
+
|
|
70
|
+
**Serve options:**
|
|
71
|
+
- `debug` - Enable debug output
|
|
72
|
+
- `port` - Server port
|
|
73
|
+
- `quiet` - Suppress output
|
|
74
|
+
|
|
75
|
+
### Paths Module
|
|
76
|
+
|
|
77
|
+
The `paths` property provides a `Paths` instance with accessor methods for Eleventy path configuration. Values are computed on access to reflect the current working directory state.
|
|
78
|
+
|
|
79
|
+
#### Naming Convention
|
|
80
|
+
|
|
81
|
+
Method names encode the return type:
|
|
82
|
+
|
|
83
|
+
| Suffix | Returns |
|
|
84
|
+
|--------|---------|
|
|
85
|
+
| `Path` or `Root` | Absolute filesystem path |
|
|
86
|
+
| `Dir` | Relative directory name |
|
|
87
|
+
|
|
88
|
+
#### API Reference
|
|
89
|
+
|
|
90
|
+
**Absolute paths:**
|
|
91
|
+
|
|
92
|
+
| Method | Description |
|
|
93
|
+
|--------|-------------|
|
|
94
|
+
| `getConfigPath()` | Absolute path to `.eleventy.js` config |
|
|
95
|
+
| `getEleventyRoot()` | Absolute path to Eleventy root directory |
|
|
96
|
+
| `getInputPath()` | Absolute path to content input directory |
|
|
97
|
+
| `getLibQuirePath()` | Absolute path to quire-11ty installation |
|
|
98
|
+
| `getProjectRoot()` | Absolute path to project root (`process.cwd()`) |
|
|
99
|
+
|
|
100
|
+
**Relative directories:**
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `getDataDir()` | Data directory relative to input (`_computed`) |
|
|
105
|
+
| `getEpubDir()` | EPUB output directory relative to Eleventy root |
|
|
106
|
+
| `getIncludesDir()` | Includes directory relative to input |
|
|
107
|
+
| `getInputDir()` | Input directory relative to Eleventy root |
|
|
108
|
+
| `getLayoutsDir()` | Layouts directory relative to input |
|
|
109
|
+
| `getOutputDir()` | Output directory relative to Eleventy root (`_site`) |
|
|
110
|
+
| `getPublicDir()` | Public assets directory (`./public`) |
|
|
111
|
+
|
|
112
|
+
**Utility:**
|
|
113
|
+
|
|
114
|
+
| Method | Description |
|
|
115
|
+
|--------|-------------|
|
|
116
|
+
| `toObject()` | Returns all paths as an object (for logging/debugging) |
|
|
117
|
+
|
|
118
|
+
### Environment Variables
|
|
119
|
+
|
|
120
|
+
The façade sets environment variables before creating the Eleventy instance:
|
|
121
|
+
|
|
122
|
+
| Variable | Source |
|
|
123
|
+
|----------|--------|
|
|
124
|
+
| `ELEVENTY_DATA` | `paths.getDataDir()` |
|
|
125
|
+
| `ELEVENTY_ENV` | `'production'` or `'development'` |
|
|
126
|
+
| `ELEVENTY_INCLUDES` | `paths.getIncludesDir()` |
|
|
127
|
+
| `ELEVENTY_LAYOUTS` | `paths.getLayoutsDir()` |
|
|
128
|
+
|
|
129
|
+
These environment variables **must be set before** the Eleventy instance is created and the `.eleventy.js` configuration is parsed.
|
|
130
|
+
|
|
131
|
+
### Design Notes
|
|
132
|
+
|
|
133
|
+
**Why a unified façade?**
|
|
134
|
+
|
|
135
|
+
The façade pattern provides:
|
|
136
|
+
1. **Single point of control** - Eleventy configuration in one place
|
|
137
|
+
2. **Consistent logging** - All operations use `[CLI:lib/11ty]` prefix
|
|
138
|
+
3. **Easy mocking** - Single object to mock in tests
|
|
139
|
+
4. **Implementation hiding** - Consumers don't need to know if we use API or CLI
|
|
140
|
+
|
|
141
|
+
**Why programmatic API instead of CLI subprocess?**
|
|
142
|
+
|
|
143
|
+
The façade uses Eleventy's programmatic API internally rather than spawning a subprocess:
|
|
144
|
+
1. **Event access** - Direct access to `eleventy.after` and other hooks
|
|
145
|
+
2. **Better error handling** - Native try/catch with full stack traces
|
|
146
|
+
3. **No spawn overhead** - Eliminates ~100-200ms process startup time
|
|
147
|
+
4. **Debugging** - Easier to trace issues in-process
|
|
148
|
+
|
|
149
|
+
The `cli.js` module is retained for backwards compatibility but is deprecated.
|
|
150
|
+
|
|
151
|
+
**Known limitation:** The Eleventy `TemplatePathResolver` requires layouts and includes directories to be within the input directory, which prevents fully decoupling Quire project content from `quire-11ty` code. See [eleventy#2655](https://github.com/11ty/eleventy/issues/2655) (still open).
|