@thegetty/quire-cli 1.0.0-rc.45 → 1.0.0-rc.47
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 +10 -27
- 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/man/quire.1 +0 -341
- package/man/quire.1.md +0 -232
- 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/project/build.js
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build detection module
|
|
3
|
-
*
|
|
4
|
-
* Detects if build output exists and provides build status information.
|
|
5
|
-
* Uses shared output path resolution to ensure consistency with the
|
|
6
|
-
* doctor command and generation commands.
|
|
7
|
-
*
|
|
8
|
-
* @module lib/project/build
|
|
9
|
-
*/
|
|
10
|
-
import fs from 'node:fs'
|
|
11
|
-
import path from 'node:path'
|
|
12
|
-
import paths from './paths.js'
|
|
13
|
-
import { getPdfOutputPaths, getEpubOutputPaths, getEpubBuildDir } from './output-paths.js'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get build output information
|
|
17
|
-
*
|
|
18
|
-
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
19
|
-
* @returns {Object} Build status information
|
|
20
|
-
*/
|
|
21
|
-
export function getBuildInfo(projectRoot = paths.getProjectRoot()) {
|
|
22
|
-
const sitePath = path.join(projectRoot, '_site')
|
|
23
|
-
const epubBuildPath = getEpubBuildDir({ projectRoot })
|
|
24
|
-
|
|
25
|
-
const info = {
|
|
26
|
-
site: {
|
|
27
|
-
exists: fs.existsSync(sitePath),
|
|
28
|
-
path: sitePath,
|
|
29
|
-
lastModified: null,
|
|
30
|
-
},
|
|
31
|
-
epub: {
|
|
32
|
-
exists: false,
|
|
33
|
-
paths: [],
|
|
34
|
-
buildDir: epubBuildPath,
|
|
35
|
-
buildDirExists: fs.existsSync(epubBuildPath),
|
|
36
|
-
lastModified: null,
|
|
37
|
-
},
|
|
38
|
-
pdf: {
|
|
39
|
-
exists: false,
|
|
40
|
-
paths: [],
|
|
41
|
-
lastModified: null,
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Get last modified times if directories exist
|
|
46
|
-
if (info.site.exists) {
|
|
47
|
-
const { mtime: lastModified } = fs.statSync(sitePath)
|
|
48
|
-
info.site.lastModified = lastModified
|
|
49
|
-
}
|
|
50
|
-
if (info.epub.buildDirExists) {
|
|
51
|
-
const { mtime: lastModified } = fs.statSync(epubBuildPath)
|
|
52
|
-
info.epub.lastModified = lastModified
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Check for EPUB files using shared path resolution
|
|
56
|
-
const epubPaths = getEpubOutputPaths({ projectRoot })
|
|
57
|
-
for (const epubPath of epubPaths) {
|
|
58
|
-
if (fs.existsSync(epubPath)) {
|
|
59
|
-
info.epub.exists = true
|
|
60
|
-
info.epub.paths.push(epubPath)
|
|
61
|
-
const { mtime: lastModified } = fs.statSync(epubPath)
|
|
62
|
-
if (!info.epub.lastModified || lastModified > info.epub.lastModified) {
|
|
63
|
-
info.epub.lastModified = lastModified
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Check for PDF files using shared path resolution
|
|
69
|
-
const pdfPaths = getPdfOutputPaths({ projectRoot })
|
|
70
|
-
for (const pdfPath of pdfPaths) {
|
|
71
|
-
if (fs.existsSync(pdfPath)) {
|
|
72
|
-
info.pdf.exists = true
|
|
73
|
-
info.pdf.paths.push(pdfPath)
|
|
74
|
-
const { mtime: lastModified } = fs.statSync(pdfPath)
|
|
75
|
-
if (!info.pdf.lastModified || lastModified > info.pdf.lastModified) {
|
|
76
|
-
info.pdf.lastModified = lastModified
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return info
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Check if site build output exists
|
|
86
|
-
*
|
|
87
|
-
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
88
|
-
* @returns {boolean} True if _site directory exists
|
|
89
|
-
*/
|
|
90
|
-
export function hasSiteOutput(projectRoot = paths.getProjectRoot()) {
|
|
91
|
-
const sitePath = path.join(projectRoot, '_site')
|
|
92
|
-
return fs.existsSync(sitePath)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Check if epub build output exists
|
|
97
|
-
*
|
|
98
|
-
* Checks for the intermediate _epub build directory.
|
|
99
|
-
*
|
|
100
|
-
* @param {string} [projectRoot] - Project root directory (defaults to cwd)
|
|
101
|
-
* @returns {boolean} True if _epub directory exists
|
|
102
|
-
*/
|
|
103
|
-
export function hasEpubOutput(projectRoot = paths.getProjectRoot()) {
|
|
104
|
-
const epubPath = getEpubBuildDir({ projectRoot })
|
|
105
|
-
return fs.existsSync(epubPath)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Check if PDF output exists
|
|
110
|
-
*
|
|
111
|
-
* Uses shared path resolution to check all possible PDF output locations,
|
|
112
|
-
* including config-aware custom paths and default engine-named paths.
|
|
113
|
-
*
|
|
114
|
-
* @param {Object} [options]
|
|
115
|
-
* @param {string} [options.lib] - Specific library to check ('pagedjs' or 'prince')
|
|
116
|
-
* @param {string} [options.projectRoot] - Project root directory
|
|
117
|
-
* @returns {boolean} True if PDF output exists
|
|
118
|
-
*/
|
|
119
|
-
export function hasPdfOutput(options = {}) {
|
|
120
|
-
const { lib, projectRoot = paths.getProjectRoot() } = options
|
|
121
|
-
|
|
122
|
-
if (lib) {
|
|
123
|
-
const pdfPath = path.join(projectRoot, `${lib}.pdf`)
|
|
124
|
-
return fs.existsSync(pdfPath)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check all possible PDF output locations
|
|
128
|
-
const pdfPaths = getPdfOutputPaths({ projectRoot })
|
|
129
|
-
return pdfPaths.some((pdfPath) => fs.existsSync(pdfPath))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Require build output to exist, throwing an error if missing
|
|
134
|
-
*
|
|
135
|
-
* @param {Object} options
|
|
136
|
-
* @param {string} [options.type='site'] - Output type to check ('site' or 'epub')
|
|
137
|
-
* @param {string} [options.projectRoot] - Project root directory
|
|
138
|
-
* @throws {Error} If required build output does not exist
|
|
139
|
-
*/
|
|
140
|
-
export function requireBuildOutput(options = {}) {
|
|
141
|
-
const { type = 'site', projectRoot = paths.getProjectRoot() } = options
|
|
142
|
-
|
|
143
|
-
if (type === 'site' && !hasSiteOutput(projectRoot)) {
|
|
144
|
-
const error = new Error('Build output not found. Run "quire build" first.')
|
|
145
|
-
error.code = 'ENOBUILD'
|
|
146
|
-
throw error
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (type === 'epub' && !hasEpubOutput(projectRoot)) {
|
|
150
|
-
const error = new Error('EPUB output not found. Run "quire build" first.')
|
|
151
|
-
error.code = 'ENOBUILD'
|
|
152
|
-
throw error
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export default {
|
|
157
|
-
getBuildInfo,
|
|
158
|
-
hasEpubOutput,
|
|
159
|
-
hasPdfOutput,
|
|
160
|
-
hasSiteOutput,
|
|
161
|
-
requireBuildOutput,
|
|
162
|
-
}
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import test from 'ava'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { Volume, createFsFromVolume } from 'memfs'
|
|
4
|
-
import esmock from 'esmock'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Helper to create cross-platform paths for memfs
|
|
8
|
-
* memfs uses forward slashes internally, but we need to pass
|
|
9
|
-
* platform-native paths to the functions under test
|
|
10
|
-
*/
|
|
11
|
-
const testRoot = '/test-project'
|
|
12
|
-
const nativePath = (...segments) => path.join(testRoot, ...segments)
|
|
13
|
-
const memfsPath = (...segments) => [testRoot, ...segments].join('/')
|
|
14
|
-
|
|
15
|
-
test.beforeEach((t) => {
|
|
16
|
-
// Create in-memory file system
|
|
17
|
-
t.context.vol = new Volume()
|
|
18
|
-
t.context.fs = createFsFromVolume(t.context.vol)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test.afterEach.always((t) => {
|
|
22
|
-
t.context.vol.reset()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('hasSiteOutput returns true when _site exists', async (t) => {
|
|
26
|
-
const { vol } = t.context
|
|
27
|
-
|
|
28
|
-
vol.fromJSON({
|
|
29
|
-
[memfsPath('_site', 'index.html')]: '<html></html>',
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
const { hasSiteOutput } = await esmock('./build.js', {
|
|
33
|
-
'node:fs': createFsFromVolume(vol),
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
t.true(hasSiteOutput(testRoot))
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
test('hasSiteOutput returns false when _site does not exist', async (t) => {
|
|
40
|
-
const { vol } = t.context
|
|
41
|
-
|
|
42
|
-
vol.fromJSON({
|
|
43
|
-
[memfsPath('.quire')]: '',
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const { hasSiteOutput } = await esmock('./build.js', {
|
|
47
|
-
'node:fs': createFsFromVolume(vol),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
t.false(hasSiteOutput(testRoot))
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test('hasEpubOutput returns true when _epub exists', async (t) => {
|
|
54
|
-
const { vol } = t.context
|
|
55
|
-
|
|
56
|
-
vol.fromJSON({
|
|
57
|
-
[memfsPath('_epub', 'content.opf')]: '<package></package>',
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
const { hasEpubOutput } = await esmock('./build.js', {
|
|
61
|
-
'node:fs': createFsFromVolume(vol),
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
t.true(hasEpubOutput(testRoot))
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test('hasEpubOutput returns false when _epub does not exist', async (t) => {
|
|
68
|
-
const { vol } = t.context
|
|
69
|
-
|
|
70
|
-
vol.fromJSON({
|
|
71
|
-
[memfsPath('.quire')]: '',
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
const { hasEpubOutput } = await esmock('./build.js', {
|
|
75
|
-
'node:fs': createFsFromVolume(vol),
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
t.false(hasEpubOutput(testRoot))
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test('getBuildInfo returns correct status for existing builds', async (t) => {
|
|
82
|
-
const { vol } = t.context
|
|
83
|
-
|
|
84
|
-
vol.fromJSON({
|
|
85
|
-
[memfsPath('_site', 'index.html')]: '<html></html>',
|
|
86
|
-
[memfsPath('_epub', 'content.opf')]: '<package></package>',
|
|
87
|
-
[memfsPath('epubjs.epub')]: 'PK\x03\x04',
|
|
88
|
-
[memfsPath('pagedjs.pdf')]: '%PDF-1.4',
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const { getBuildInfo } = await esmock('./build.js', {
|
|
92
|
-
'node:fs': createFsFromVolume(vol),
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const info = getBuildInfo(testRoot)
|
|
96
|
-
|
|
97
|
-
t.true(info.site.exists)
|
|
98
|
-
t.is(info.site.path, nativePath('_site'))
|
|
99
|
-
t.truthy(info.site.lastModified)
|
|
100
|
-
|
|
101
|
-
t.true(info.epub.exists)
|
|
102
|
-
t.true(info.epub.buildDirExists)
|
|
103
|
-
t.is(info.epub.buildDir, nativePath('_epub'))
|
|
104
|
-
t.true(info.epub.paths.includes(nativePath('epubjs.epub')))
|
|
105
|
-
t.truthy(info.epub.lastModified)
|
|
106
|
-
|
|
107
|
-
t.true(info.pdf.exists)
|
|
108
|
-
t.deepEqual(info.pdf.paths, [nativePath('pagedjs.pdf')])
|
|
109
|
-
t.truthy(info.pdf.lastModified)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('getBuildInfo returns correct status for missing builds', async (t) => {
|
|
113
|
-
const { vol } = t.context
|
|
114
|
-
|
|
115
|
-
vol.fromJSON({
|
|
116
|
-
[memfsPath('.quire')]: '',
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
const { getBuildInfo } = await esmock('./build.js', {
|
|
120
|
-
'node:fs': createFsFromVolume(vol),
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
const info = getBuildInfo(testRoot)
|
|
124
|
-
|
|
125
|
-
t.false(info.site.exists)
|
|
126
|
-
t.is(info.site.lastModified, null)
|
|
127
|
-
|
|
128
|
-
t.false(info.epub.exists)
|
|
129
|
-
t.false(info.epub.buildDirExists)
|
|
130
|
-
t.is(info.epub.lastModified, null)
|
|
131
|
-
|
|
132
|
-
t.false(info.pdf.exists)
|
|
133
|
-
t.deepEqual(info.pdf.paths, [])
|
|
134
|
-
t.is(info.pdf.lastModified, null)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
test('getBuildInfo returns multiple PDF paths when both exist', async (t) => {
|
|
138
|
-
const { vol } = t.context
|
|
139
|
-
|
|
140
|
-
vol.fromJSON({
|
|
141
|
-
[memfsPath('pagedjs.pdf')]: '%PDF-1.4',
|
|
142
|
-
[memfsPath('prince.pdf')]: '%PDF-1.4',
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const { getBuildInfo } = await esmock('./build.js', {
|
|
146
|
-
'node:fs': createFsFromVolume(vol),
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
const info = getBuildInfo(testRoot)
|
|
150
|
-
|
|
151
|
-
t.true(info.pdf.exists)
|
|
152
|
-
t.is(info.pdf.paths.length, 2)
|
|
153
|
-
t.true(info.pdf.paths.includes(nativePath('pagedjs.pdf')))
|
|
154
|
-
t.true(info.pdf.paths.includes(nativePath('prince.pdf')))
|
|
155
|
-
t.truthy(info.pdf.lastModified)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test('getBuildInfo detects epub build dir without .epub files', async (t) => {
|
|
159
|
-
const { vol } = t.context
|
|
160
|
-
|
|
161
|
-
vol.fromJSON({
|
|
162
|
-
[memfsPath('_epub', 'content.opf')]: '<package></package>',
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
const { getBuildInfo } = await esmock('./build.js', {
|
|
166
|
-
'node:fs': createFsFromVolume(vol),
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
const info = getBuildInfo(testRoot)
|
|
170
|
-
|
|
171
|
-
t.false(info.epub.exists, 'no .epub files should mean exists is false')
|
|
172
|
-
t.true(info.epub.buildDirExists, '_epub directory should be detected')
|
|
173
|
-
t.truthy(info.epub.lastModified, 'lastModified from _epub dir should be present')
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
test('requireBuildOutput throws when site output missing', async (t) => {
|
|
177
|
-
const { vol } = t.context
|
|
178
|
-
|
|
179
|
-
vol.fromJSON({
|
|
180
|
-
[memfsPath('.quire')]: '',
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const { requireBuildOutput } = await esmock('./build.js', {
|
|
184
|
-
'node:fs': createFsFromVolume(vol),
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
const error = t.throws(() => {
|
|
188
|
-
requireBuildOutput({ type: 'site', projectRoot: testRoot })
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
t.is(error.code, 'ENOBUILD')
|
|
192
|
-
t.regex(error.message, /quire build/)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
test('requireBuildOutput throws when epub output missing', async (t) => {
|
|
196
|
-
const { vol } = t.context
|
|
197
|
-
|
|
198
|
-
vol.fromJSON({
|
|
199
|
-
[memfsPath('.quire')]: '',
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
const { requireBuildOutput } = await esmock('./build.js', {
|
|
203
|
-
'node:fs': createFsFromVolume(vol),
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
const error = t.throws(() => {
|
|
207
|
-
requireBuildOutput({ type: 'epub', projectRoot: testRoot })
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
t.is(error.code, 'ENOBUILD')
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
test('requireBuildOutput does not throw when output exists', async (t) => {
|
|
214
|
-
const { vol } = t.context
|
|
215
|
-
|
|
216
|
-
vol.fromJSON({
|
|
217
|
-
[memfsPath('_site', 'index.html')]: '<html></html>',
|
|
218
|
-
[memfsPath('_epub', 'content.opf')]: '<package></package>',
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
const { requireBuildOutput } = await esmock('./build.js', {
|
|
222
|
-
'node:fs': createFsFromVolume(vol),
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
t.notThrows(() => {
|
|
226
|
-
requireBuildOutput({ type: 'site', projectRoot: testRoot })
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
t.notThrows(() => {
|
|
230
|
-
requireBuildOutput({ type: 'epub', projectRoot: testRoot })
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
test('hasPdfOutput returns true when specific lib PDF exists', async (t) => {
|
|
235
|
-
const { vol } = t.context
|
|
236
|
-
|
|
237
|
-
vol.fromJSON({
|
|
238
|
-
[memfsPath('pagedjs.pdf')]: '%PDF-1.4',
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
const { hasPdfOutput } = await esmock('./build.js', {
|
|
242
|
-
'node:fs': createFsFromVolume(vol),
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
t.true(hasPdfOutput({ lib: 'pagedjs', projectRoot: testRoot }))
|
|
246
|
-
t.false(hasPdfOutput({ lib: 'prince', projectRoot: testRoot }))
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
test('hasPdfOutput returns false when no PDF exists', async (t) => {
|
|
250
|
-
const { vol } = t.context
|
|
251
|
-
|
|
252
|
-
vol.fromJSON({
|
|
253
|
-
[memfsPath('.quire')]: '',
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
const { hasPdfOutput } = await esmock('./build.js', {
|
|
257
|
-
'node:fs': createFsFromVolume(vol),
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
t.false(hasPdfOutput({ projectRoot: testRoot }))
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
test('hasPdfOutput returns true when any common PDF exists', async (t) => {
|
|
264
|
-
const { vol } = t.context
|
|
265
|
-
|
|
266
|
-
vol.fromJSON({
|
|
267
|
-
[memfsPath('prince.pdf')]: '%PDF-1.4',
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
const { hasPdfOutput } = await esmock('./build.js', {
|
|
271
|
-
'node:fs': createFsFromVolume(vol),
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
t.true(hasPdfOutput({ projectRoot: testRoot }))
|
|
275
|
-
})
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project configuration module
|
|
3
|
-
*
|
|
4
|
-
* Loads and validates Quire project configuration files.
|
|
5
|
-
*
|
|
6
|
-
* @module lib/project/config
|
|
7
|
-
*/
|
|
8
|
-
import fs from 'fs-extra'
|
|
9
|
-
import path from 'node:path'
|
|
10
|
-
import { pathToFileURL } from 'node:url'
|
|
11
|
-
import yaml from 'js-yaml'
|
|
12
|
-
import paths from './paths.js'
|
|
13
|
-
import { ConfigFileNotFoundError } from '#src/errors/index.js'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Load and validate Quire project configuration
|
|
17
|
-
*
|
|
18
|
-
* @param {string} [projectRoot] - project root directory (defaults to cwd)
|
|
19
|
-
* @returns {Promise<Object>} validated configuration object
|
|
20
|
-
*
|
|
21
|
-
* @todo consider hardcoding a version check against .quire / project's package.json ver
|
|
22
|
-
*/
|
|
23
|
-
export async function loadProjectConfig(projectRoot) {
|
|
24
|
-
const root = projectRoot || paths.getProjectRoot()
|
|
25
|
-
const configPath = path.join(root, 'content', '_data', 'config.yaml')
|
|
26
|
-
|
|
27
|
-
if (!fs.existsSync(configPath)) {
|
|
28
|
-
throw new ConfigFileNotFoundError(configPath)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const data = fs.readFileSync(configPath)
|
|
32
|
-
let config = yaml.load(data)
|
|
33
|
-
|
|
34
|
-
// NB: Schemas and validators are specific to the 11ty version of the project being built
|
|
35
|
-
const schemaPath = path.join(root, '_plugins', 'schemas', 'config.json')
|
|
36
|
-
const validatorPath = path.join(root, '_plugins', 'globalData', 'validator.js')
|
|
37
|
-
|
|
38
|
-
if (fs.existsSync(schemaPath) && fs.existsSync(validatorPath)) {
|
|
39
|
-
const { validateUserConfig } = await import(pathToFileURL(validatorPath))
|
|
40
|
-
|
|
41
|
-
const schemaJSON = fs.readFileSync(schemaPath)
|
|
42
|
-
const schema = JSON.parse(schemaJSON)
|
|
43
|
-
|
|
44
|
-
config = validateUserConfig('config', config, { config: schema })
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return config
|
|
48
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import test from 'ava'
|
|
2
|
-
import { Volume, createFsFromVolume } from 'memfs'
|
|
3
|
-
import sinon from 'sinon'
|
|
4
|
-
import esmock from 'esmock'
|
|
5
|
-
|
|
6
|
-
test.beforeEach((t) => {
|
|
7
|
-
// Create sinon sandbox for mocking
|
|
8
|
-
t.context.sandbox = sinon.createSandbox()
|
|
9
|
-
|
|
10
|
-
// Create in-memory file system
|
|
11
|
-
t.context.vol = new Volume()
|
|
12
|
-
t.context.fs = createFsFromVolume(t.context.vol)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test.afterEach.always((t) => {
|
|
16
|
-
// Restore all mocks
|
|
17
|
-
if (t.context.sandbox) {
|
|
18
|
-
t.context.sandbox.restore()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Clear in-memory file system
|
|
22
|
-
t.context.vol.reset()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('loadProjectConfig should throw ConfigFileNotFoundError when config file does not exist', async (t) => {
|
|
26
|
-
const { fs, vol } = t.context
|
|
27
|
-
|
|
28
|
-
// Setup empty directory
|
|
29
|
-
vol.fromJSON({
|
|
30
|
-
'/project/content/_data/.gitkeep': ''
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const { loadProjectConfig } = await esmock('./config.js', {
|
|
34
|
-
'fs-extra': {
|
|
35
|
-
existsSync: (p) => fs.existsSync(p),
|
|
36
|
-
readFileSync: (p) => fs.readFileSync(p)
|
|
37
|
-
},
|
|
38
|
-
'./paths.js': {
|
|
39
|
-
default: {
|
|
40
|
-
getProjectRoot: () => '/project'
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
const error = await t.throwsAsync(() => loadProjectConfig('/project'))
|
|
46
|
-
|
|
47
|
-
t.is(error.code, 'CONFIG_FILE_NOT_FOUND')
|
|
48
|
-
t.true(error.message.includes('config.yaml'), 'error should include config path')
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
test('loadProjectConfig should load and parse YAML config file', async (t) => {
|
|
52
|
-
const { fs, vol } = t.context
|
|
53
|
-
|
|
54
|
-
// Setup config file
|
|
55
|
-
vol.fromJSON({
|
|
56
|
-
'/project/content/_data/config.yaml': 'title: Test Project\nauthor: Test Author'
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
// Mock fs-extra to use memfs
|
|
60
|
-
const { loadProjectConfig } = await esmock('./config.js', {
|
|
61
|
-
'fs-extra': {
|
|
62
|
-
existsSync: (p) => fs.existsSync(p),
|
|
63
|
-
readFileSync: (p) => fs.readFileSync(p)
|
|
64
|
-
},
|
|
65
|
-
'./paths.js': {
|
|
66
|
-
default: {
|
|
67
|
-
getProjectRoot: () => '/project'
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const result = await loadProjectConfig('/project')
|
|
73
|
-
|
|
74
|
-
t.truthy(result, 'should return config object')
|
|
75
|
-
t.is(result.title, 'Test Project', 'should parse title')
|
|
76
|
-
t.is(result.author, 'Test Author', 'should parse author')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('loadProjectConfig should load PDF configuration', async (t) => {
|
|
80
|
-
const { fs, vol } = t.context
|
|
81
|
-
|
|
82
|
-
// Setup config file with PDF settings
|
|
83
|
-
vol.fromJSON({
|
|
84
|
-
'/project/content/_data/config.yaml': `title: Test Project
|
|
85
|
-
pdf:
|
|
86
|
-
filename: test-book
|
|
87
|
-
outputDir: _pdf`
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// Mock fs-extra to use memfs
|
|
91
|
-
const { loadProjectConfig } = await esmock('./config.js', {
|
|
92
|
-
'fs-extra': {
|
|
93
|
-
existsSync: (p) => fs.existsSync(p),
|
|
94
|
-
readFileSync: (p) => fs.readFileSync(p)
|
|
95
|
-
},
|
|
96
|
-
'./paths.js': {
|
|
97
|
-
default: {
|
|
98
|
-
getProjectRoot: () => '/project'
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
const result = await loadProjectConfig('/project')
|
|
104
|
-
|
|
105
|
-
t.truthy(result.pdf, 'should have pdf config')
|
|
106
|
-
t.is(result.pdf.filename, 'test-book', 'should parse pdf filename')
|
|
107
|
-
t.is(result.pdf.outputDir, '_pdf', 'should parse pdf outputDir')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test('loadProjectConfig should use default project root when not provided', async (t) => {
|
|
111
|
-
const { fs, vol } = t.context
|
|
112
|
-
|
|
113
|
-
// Setup config file
|
|
114
|
-
vol.fromJSON({
|
|
115
|
-
'/default/project/content/_data/config.yaml': 'title: Default Project'
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
// Mock fs-extra to use memfs
|
|
119
|
-
const { loadProjectConfig } = await esmock('./config.js', {
|
|
120
|
-
'fs-extra': {
|
|
121
|
-
existsSync: (p) => fs.existsSync(p),
|
|
122
|
-
readFileSync: (p) => fs.readFileSync(p)
|
|
123
|
-
},
|
|
124
|
-
'./paths.js': {
|
|
125
|
-
default: {
|
|
126
|
-
getProjectRoot: () => '/default/project'
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
const result = await loadProjectConfig()
|
|
132
|
-
|
|
133
|
-
t.is(result.title, 'Default Project', 'should use default project root')
|
|
134
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project detection module
|
|
3
|
-
*
|
|
4
|
-
* Detects if a directory is a Quire project root by checking for marker files.
|
|
5
|
-
*
|
|
6
|
-
* @module lib/project/detect
|
|
7
|
-
*/
|
|
8
|
-
import fs from 'node:fs'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Files that indicate a Quire project directory.
|
|
12
|
-
*
|
|
13
|
-
* These marker files are created by `quire new` or exist in Quire project templates.
|
|
14
|
-
* The presence of any one of these files indicates the directory is a Quire project root.
|
|
15
|
-
*
|
|
16
|
-
* @constant {ReadonlyArray<string>}
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* // Check if a file is a project marker
|
|
20
|
-
* if (PROJECT_MARKERS.includes(filename)) {
|
|
21
|
-
* console.log('This is a Quire project root')
|
|
22
|
-
* }
|
|
23
|
-
*/
|
|
24
|
-
export const PROJECT_MARKERS = Object.freeze([
|
|
25
|
-
'.eleventy.js',
|
|
26
|
-
'.quire',
|
|
27
|
-
'.quire-11ty',
|
|
28
|
-
'.quire-version',
|
|
29
|
-
'eleventy.config.js',
|
|
30
|
-
])
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Test if a directory is a Quire project root
|
|
34
|
-
*
|
|
35
|
-
* Nota bene: `dirpath` must be to the Quire project root to positively confirm
|
|
36
|
-
* a directory is a Quire project, testing a Quire project subdirectory will
|
|
37
|
-
* return falsely negative result.
|
|
38
|
-
*
|
|
39
|
-
* @todo refactor this to throw an Error NOTQUIRE
|
|
40
|
-
*
|
|
41
|
-
* @param {String} dirpath path to a local directory
|
|
42
|
-
* @return {Promise}
|
|
43
|
-
*/
|
|
44
|
-
export default function (dirpath) {
|
|
45
|
-
return fs.readdirSync(dirpath)
|
|
46
|
-
.find((entry) => PROJECT_MARKERS.includes(entry))
|
|
47
|
-
}
|