@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.
Files changed (245) hide show
  1. package/CHANGELOG.md +0 -36
  2. package/README.md +0 -9
  3. package/bin/cli.js +1 -46
  4. package/package.json +10 -27
  5. package/src/Command.js +6 -26
  6. package/src/commands/README.md +122 -213
  7. package/src/commands/build.js +37 -45
  8. package/src/commands/clean.js +19 -33
  9. package/src/commands/conf.js +43 -0
  10. package/src/commands/create.js +14 -42
  11. package/src/commands/epub.js +31 -67
  12. package/src/commands/index.js +2 -9
  13. package/src/commands/info.js +86 -88
  14. package/src/commands/pdf.js +82 -63
  15. package/src/commands/preview.js +31 -32
  16. package/src/commands/validate.js +20 -64
  17. package/src/commands/version.js +43 -0
  18. package/src/errors/validation/validation-error.js +11 -20
  19. package/src/helpers/clean.js +1 -1
  20. package/src/helpers/is-quire.js +31 -0
  21. package/src/helpers/test-cwd.js +6 -5
  22. package/src/helpers/which.js +4 -10
  23. package/src/lib/11ty/README.md +19 -135
  24. package/src/lib/11ty/api.js +93 -235
  25. package/src/lib/11ty/cli.js +37 -112
  26. package/src/lib/11ty/index.js +5 -64
  27. package/src/lib/11ty/paths.js +103 -0
  28. package/src/lib/README.md +0 -330
  29. package/src/lib/conf/README.md +69 -86
  30. package/src/lib/conf/config.js +3 -5
  31. package/src/lib/conf/defaults.js +0 -65
  32. package/src/lib/conf/schema.js +9 -103
  33. package/src/lib/epub/README.md +2 -133
  34. package/src/lib/epub/epub.js +11 -36
  35. package/src/lib/epub/index.js +21 -111
  36. package/src/lib/epub/pandoc.js +4 -33
  37. package/src/lib/git/README.md +2 -151
  38. package/src/lib/git/index.js +13 -217
  39. package/src/lib/i18n/README.md +3 -0
  40. package/src/lib/i18n/config.js +53 -0
  41. package/src/lib/i18n/index.js +43 -0
  42. package/src/lib/i18n/localeService.js +58 -0
  43. package/src/lib/pdf/README.md +0 -131
  44. package/src/lib/pdf/index.js +21 -124
  45. package/src/lib/pdf/paged.js +52 -100
  46. package/src/lib/pdf/prince.js +37 -115
  47. package/src/lib/pdf/split.js +33 -61
  48. package/src/lib/quire/README.md +19 -0
  49. package/src/lib/quire/index.js +353 -0
  50. package/src/lib/reporter/README.md +2 -211
  51. package/src/lib/reporter/index.js +0 -607
  52. package/src/main.js +48 -221
  53. package/src/packageConfig.js +0 -17
  54. package/src/validators/utils.js +1 -2
  55. package/man/quire.1 +0 -341
  56. package/man/quire.1.md +0 -232
  57. package/schemas/config.schema.json +0 -194
  58. package/schemas/figures.schema.json +0 -56
  59. package/schemas/layout.schema.json +0 -5
  60. package/schemas/objects.schema.json +0 -62
  61. package/schemas/publication.schema.json +0 -140
  62. package/schemas/references.schema.json +0 -29
  63. package/src/Command.spec.js +0 -99
  64. package/src/commands/build.spec.js +0 -113
  65. package/src/commands/build.test.js +0 -397
  66. package/src/commands/clean.spec.js +0 -118
  67. package/src/commands/clean.test.js +0 -382
  68. package/src/commands/config.js +0 -238
  69. package/src/commands/config.spec.js +0 -107
  70. package/src/commands/config.test.js +0 -845
  71. package/src/commands/create.spec.js +0 -112
  72. package/src/commands/create.test.js +0 -415
  73. package/src/commands/doctor.js +0 -251
  74. package/src/commands/doctor.spec.js +0 -115
  75. package/src/commands/doctor.test.js +0 -1409
  76. package/src/commands/epub.spec.js +0 -114
  77. package/src/commands/epub.test.js +0 -503
  78. package/src/commands/help.js +0 -60
  79. package/src/commands/help.test.js +0 -132
  80. package/src/commands/info.spec.js +0 -72
  81. package/src/commands/info.test.js +0 -512
  82. package/src/commands/pdf.spec.js +0 -114
  83. package/src/commands/pdf.test.js +0 -464
  84. package/src/commands/preview.spec.js +0 -97
  85. package/src/commands/preview.test.js +0 -250
  86. package/src/commands/use.js +0 -56
  87. package/src/commands/use.spec.js +0 -61
  88. package/src/commands/use.test.js +0 -280
  89. package/src/commands/validate.spec.js +0 -90
  90. package/src/commands/validate.test.js +0 -378
  91. package/src/commands/workflows.js +0 -70
  92. package/src/errors/build/build-failed-error.js +0 -19
  93. package/src/errors/build/config-field-missing-error.js +0 -20
  94. package/src/errors/build/config-file-not-found-error.js +0 -20
  95. package/src/errors/build/index.js +0 -11
  96. package/src/errors/help/help-topic-not-found-error.js +0 -30
  97. package/src/errors/help/index.js +0 -8
  98. package/src/errors/index.js +0 -58
  99. package/src/errors/input/index.js +0 -8
  100. package/src/errors/input/invalid-input-error.js +0 -21
  101. package/src/errors/install/dependency-install-error.js +0 -19
  102. package/src/errors/install/directory-not-empty-error.js +0 -25
  103. package/src/errors/install/index.js +0 -12
  104. package/src/errors/install/invalid-path-error.js +0 -31
  105. package/src/errors/install/invalid-starter-error.js +0 -27
  106. package/src/errors/install/version-not-found-error.js +0 -35
  107. package/src/errors/output/epub-generation-error.js +0 -19
  108. package/src/errors/output/index.js +0 -14
  109. package/src/errors/output/invalid-epub-library-error.js +0 -20
  110. package/src/errors/output/invalid-pdf-library-error.js +0 -20
  111. package/src/errors/output/missing-build-output-error.js +0 -21
  112. package/src/errors/output/pdf-generation-error.js +0 -24
  113. package/src/errors/output/tool-not-found-error.js +0 -37
  114. package/src/errors/project/index.js +0 -10
  115. package/src/errors/project/not-in-project-error.js +0 -20
  116. package/src/errors/project/project-create-error.js +0 -21
  117. package/src/errors/quire-error.js +0 -27
  118. package/src/errors/validation/index.js +0 -3
  119. package/src/errors/validation/unknown-config-key-error.js +0 -27
  120. package/src/errors/validation/unknown-config-operation-error.js +0 -24
  121. package/src/helpers/docs-url.js +0 -32
  122. package/src/helpers/pager.js +0 -59
  123. package/src/helpers/pager.test.js +0 -85
  124. package/src/helpers/suggest-similar.js +0 -124
  125. package/src/helpers/suggest-similar.test.js +0 -108
  126. package/src/helpers/test-cwd.test.js +0 -192
  127. package/src/lib/11ty/index.test.js +0 -655
  128. package/src/lib/commander/index.js +0 -103
  129. package/src/lib/commander/index.test.js +0 -86
  130. package/src/lib/commander/options.js +0 -237
  131. package/src/lib/commander/options.test.js +0 -205
  132. package/src/lib/conf/build-status.js +0 -103
  133. package/src/lib/conf/build-status.test.js +0 -247
  134. package/src/lib/conf/config.test.js +0 -281
  135. package/src/lib/conf/format.js +0 -60
  136. package/src/lib/conf/format.test.js +0 -106
  137. package/src/lib/conf/helpers.js +0 -91
  138. package/src/lib/conf/helpers.test.js +0 -136
  139. package/src/lib/conf/index.js +0 -22
  140. package/src/lib/constants.js +0 -28
  141. package/src/lib/doctor/README.md +0 -667
  142. package/src/lib/doctor/checks/environment/cli-version.js +0 -62
  143. package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
  144. package/src/lib/doctor/checks/environment/git-available.js +0 -77
  145. package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
  146. package/src/lib/doctor/checks/environment/index.js +0 -13
  147. package/src/lib/doctor/checks/environment/node-version.js +0 -66
  148. package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
  149. package/src/lib/doctor/checks/environment/npm-available.js +0 -66
  150. package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
  151. package/src/lib/doctor/checks/environment/os-info.js +0 -48
  152. package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
  153. package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
  154. package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
  155. package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
  156. package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
  157. package/src/lib/doctor/checks/outputs/index.js +0 -10
  158. package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
  159. package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
  160. package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
  161. package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
  162. package/src/lib/doctor/checks/project/data-files.js +0 -56
  163. package/src/lib/doctor/checks/project/data-files.test.js +0 -125
  164. package/src/lib/doctor/checks/project/dependencies.js +0 -53
  165. package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
  166. package/src/lib/doctor/checks/project/index.js +0 -11
  167. package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
  168. package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
  169. package/src/lib/doctor/checks/project/quire-project.js +0 -38
  170. package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
  171. package/src/lib/doctor/checks/tools/index.js +0 -10
  172. package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
  173. package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
  174. package/src/lib/doctor/checks/tools/prince-available.js +0 -81
  175. package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
  176. package/src/lib/doctor/constants.js +0 -39
  177. package/src/lib/doctor/formatDuration.js +0 -108
  178. package/src/lib/doctor/formatDuration.test.js +0 -76
  179. package/src/lib/doctor/formatters/human.js +0 -257
  180. package/src/lib/doctor/formatters/human.test.js +0 -463
  181. package/src/lib/doctor/formatters/index.js +0 -8
  182. package/src/lib/doctor/formatters/json.js +0 -78
  183. package/src/lib/doctor/formatters/json.test.js +0 -174
  184. package/src/lib/doctor/formatters/shared.js +0 -129
  185. package/src/lib/doctor/formatters/shared.test.js +0 -194
  186. package/src/lib/doctor/index.js +0 -271
  187. package/src/lib/doctor/index.test.js +0 -797
  188. package/src/lib/epub/engines.js +0 -46
  189. package/src/lib/epub/index.test.js +0 -518
  190. package/src/lib/epub/pandoc.test.js +0 -122
  191. package/src/lib/epub/schema.js +0 -21
  192. package/src/lib/error/README.md +0 -170
  193. package/src/lib/error/handler.js +0 -107
  194. package/src/lib/git/index.spec.js +0 -80
  195. package/src/lib/git/index.test.js +0 -453
  196. package/src/lib/help/frontmatter.js +0 -77
  197. package/src/lib/help/frontmatter.test.js +0 -139
  198. package/src/lib/help/index.js +0 -146
  199. package/src/lib/help/index.test.js +0 -188
  200. package/src/lib/help/topics/configuration.md +0 -77
  201. package/src/lib/help/topics/debugging.md +0 -69
  202. package/src/lib/help/topics/epub.md +0 -74
  203. package/src/lib/help/topics/pdf.md +0 -74
  204. package/src/lib/help/topics/publishing.md +0 -80
  205. package/src/lib/help/topics/workflows.md +0 -50
  206. package/src/lib/installer/index.js +0 -309
  207. package/src/lib/installer/index.spec.js +0 -83
  208. package/src/lib/installer/index.test.js +0 -545
  209. package/src/lib/logger/README.md +0 -424
  210. package/src/lib/logger/debug.js +0 -132
  211. package/src/lib/logger/debug.spec.js +0 -130
  212. package/src/lib/logger/debug.test.js +0 -128
  213. package/src/lib/logger/index.js +0 -228
  214. package/src/lib/logger/index.spec.js +0 -131
  215. package/src/lib/logger/index.test.js +0 -578
  216. package/src/lib/npm/README.md +0 -127
  217. package/src/lib/npm/index.js +0 -198
  218. package/src/lib/npm/index.spec.js +0 -60
  219. package/src/lib/npm/index.test.js +0 -355
  220. package/src/lib/pdf/engines.js +0 -46
  221. package/src/lib/pdf/index.test.js +0 -708
  222. package/src/lib/pdf/paged.test.js +0 -366
  223. package/src/lib/pdf/prince.test.js +0 -202
  224. package/src/lib/pdf/schema.js +0 -21
  225. package/src/lib/pdf/split.test.js +0 -445
  226. package/src/lib/platform.js +0 -95
  227. package/src/lib/process/manager.js +0 -110
  228. package/src/lib/process/manager.test.js +0 -55
  229. package/src/lib/project/build.js +0 -162
  230. package/src/lib/project/build.test.js +0 -275
  231. package/src/lib/project/config.js +0 -48
  232. package/src/lib/project/config.test.js +0 -134
  233. package/src/lib/project/detect.js +0 -47
  234. package/src/lib/project/detect.test.js +0 -157
  235. package/src/lib/project/index.js +0 -56
  236. package/src/lib/project/output-paths.js +0 -87
  237. package/src/lib/project/output-paths.test.js +0 -66
  238. package/src/lib/project/paths.js +0 -272
  239. package/src/lib/project/version.js +0 -110
  240. package/src/lib/project/version.test.js +0 -350
  241. package/src/lib/reporter/index.test.js +0 -862
  242. package/src/main.spec.js +0 -61
  243. package/src/main.test.js +0 -347
  244. package/src/validators/validate-data-files.js +0 -154
  245. package/src/validators/validate-data-files.test.js +0 -217
@@ -1,89 +1,61 @@
1
1
  import path from 'node:path'
2
+
2
3
  import { PDFDocument } from 'pdf-lib'
3
4
  import { paths } from '#lib/11ty/index.js'
4
- import { PdfGenerationError } from '#src/errors/index.js'
5
- import createDebug from '#debug'
6
-
7
- const debug = createDebug('lib:pdf:split')
8
-
5
+
9
6
  /**
10
- * Split a PDF into sections based on a page map
11
- *
7
+ * @function splitPdf(file,pageMap) -- sections out individual PDFs from `file` according to `pageMap`
8
+ *
12
9
  * @param {ArrayBuffer} file - PDF file to split
13
- * @param {ArrayBuffer} [coversFile] - Optional covers PDF file
14
- * @param {Object} pageMap - Map of page IDs to page ranges
15
- * @param {Object} [pdfConfig] - PDF configuration from project config
16
- * @returns {Promise<Object>} Map of file paths to PDF binary data
17
- * @throws {PdfGenerationError} When PDF loading or manipulation fails
10
+ * @param {Object} pageMap - page map to split PDf by
11
+ *
12
+ * Creates individual PDFs from by copying `file` (so boxes are already set) and stripping pages out of the range (in reverse order to retain the index sequence)
13
+ * Returns a map of file paths to PDF binary data for serialization
18
14
  */
19
- export async function splitPdf(file, coversFile, pageMap, pdfConfig) {
20
15
 
21
- if (!pdfConfig) {
22
- debug('No pdfConfig provided, PDF splitting skipped.')
16
+ export async function splitPdf(file,coversFile,pageMap,pdfConfig) {
17
+
18
+ if (!pdfConfig) {
23
19
  return {}
24
20
  }
25
21
 
26
22
  const { filename, outputDir } = pdfConfig
27
23
 
28
- debug('loading main PDF')
29
- let pdfDoc
30
- try {
31
- pdfDoc = await PDFDocument.load(file)
32
- } catch (error) {
33
- throw new PdfGenerationError('pdf-lib', 'load main PDF', error.message)
34
- }
35
- debug('main PDF loaded: %d pages', pdfDoc.getPageCount())
24
+ const pdfDoc = await PDFDocument.load(file)
36
25
 
37
- let coversDoc
38
- if (coversFile !== undefined) {
39
- debug('loading covers PDF')
40
- try {
41
- coversDoc = await PDFDocument.load(coversFile)
42
- } catch (error) {
43
- throw new PdfGenerationError('pdf-lib', 'load covers PDF', error.message)
44
- }
45
- debug('covers PDF loaded: %d pages', coversDoc.getPageCount())
46
- }
26
+ const coversDoc = (coversFile !== undefined) ? await PDFDocument.load(coversFile) : undefined
47
27
 
48
28
  let resultFiles = {}
49
- const pageEntries = Object.entries(pageMap)
50
- debug('splitting into %d sections', pageEntries.length)
51
29
 
52
- for (const [pageId, pageConfig] of pageEntries) {
30
+ for ( const [pageId, pageConfig] of Object.entries(pageMap) ) {
53
31
  const { endPage, startPage, coverPage } = pageConfig
54
32
 
55
- try {
56
- // TODO: Set the PDF's sectional doc metadata
57
-
58
- const sectionDoc = await pdfDoc.copy()
33
+ // TODO: Set the PDF's sectional doc metadata
34
+
35
+ const sectionDoc = await pdfDoc.copy()
59
36
 
60
- for (let p = pdfDoc.getPageCount() - 1; p > endPage; --p) {
61
- sectionDoc.removePage(p)
62
- }
63
- for (let q = startPage - 1; q >= 0; --q) {
64
- sectionDoc.removePage(q)
65
- }
37
+ for (let p=pdfDoc.getPageCount() - 1; p > endPage; --p) {
38
+ sectionDoc.removePage(p)
39
+ }
40
+ for (let q=startPage - 1; q >= 0; --q) {
41
+ sectionDoc.removePage(q)
42
+ }
66
43
 
67
- if (coversDoc && coverPage !== undefined && coverPage >= 0) {
68
- // copyPages sets page sizing and other metadata on the target PDF
69
- const cover = await sectionDoc.copyPages(coversDoc, [coverPage])
70
- sectionDoc.insertPage(0, cover[0])
71
- }
44
+ if (coversDoc && coverPage !== undefined && coverPage >= 0) {
45
+ // NB: `copyPages()` sets page sizing + other metadata on the way into the target PDF
46
+ const cover = await sectionDoc.copyPages(coversDoc,[coverPage])
47
+ sectionDoc.insertPage(0,cover[0])
48
+ }
72
49
 
73
- const section = await sectionDoc.save()
50
+ const section = await sectionDoc.save()
74
51
 
75
- const sectionId = pageId.replace(/^page-/g, '')
76
- const sectionFn = `${filename}-${sectionId}.pdf`
77
- const sectionFp = path.join(paths.getOutputDir(), outputDir, sectionFn)
52
+ const sectionId = pageId.replace(/^page-/g,'')
53
+ const sectionFn = `${filename}-${sectionId}.pdf`
54
+ const sectionFp = path.join( paths.output, outputDir, sectionFn )
78
55
 
79
- resultFiles[sectionFp] = section
80
- debug('extracted section %s (pages %d-%d)', sectionId, startPage, endPage)
81
- } catch (error) {
82
- throw new PdfGenerationError('pdf-lib', `extract section '${pageId}'`, error.message)
83
- }
56
+ resultFiles[sectionFp] = section
84
57
  }
85
58
 
86
- debug('split complete: %d files', Object.keys(resultFiles).length)
87
59
  return resultFiles
88
60
  }
89
61
 
@@ -0,0 +1,19 @@
1
+ ## CLI Quire Module
2
+
3
+ The primary concern of the Quire CLI `quire` module is to decouple the `11ty` package from the publication content templates and manage the version of the [Quire/11ty package](packages/11ty) that is used to build a Quire project.
4
+
5
+ The [Quire/11ty package](packages/11ty) contains the Eleventy [configuration](packages/11ty/.eleventy.js), [includes](packages/11ty/_includes), [layouts](packages/11ty/_layouts), and [plugins](packages/11ty/_plugins) for [universal template shortcodes](packages/11ty/_plugins/shortcodes) and [components](packages/11ty/_includes/components) to build Quire publications.
6
+
7
+ When the Quire CLI is installed the latest version of the [11ty package](packages/11ty) is installed in in the `cli/quire/versions/` directory.
8
+
9
+ ```sh
10
+ cli/
11
+ quire/
12
+ versions/
13
+ 1.0.0/
14
+ _includes/
15
+ _layouts/
16
+ _plugins/
17
+ .eleventy.js
18
+ package.json
19
+ ```
@@ -0,0 +1,353 @@
1
+ import { IS_WINDOWS } from '#helpers/os-utils.js'
2
+ import { chdir, cwd } from 'node:process'
3
+ import { execa, execaCommand } from 'execa'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { isEmpty } from '#helpers/is-empty.js'
6
+ import config from '#lib/conf/config.js'
7
+ import fetch from 'node-fetch'
8
+ import fs from 'fs-extra'
9
+ import git from '#lib/git/index.js'
10
+ import packageConfig from '#src/packageConfig.js'
11
+ import path from 'node:path'
12
+ import semver from 'semver'
13
+
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const __dirname = path.dirname(__filename)
16
+
17
+ // Version install path is relative to process working directory
18
+ const INSTALL_PATH = path.join('src', 'lib', 'quire', 'versions')
19
+ const PACKAGE_NAME = '@thegetty/quire-11ty'
20
+
21
+ const QUIRE_VERSION = config.get('quireVersion')
22
+ const VERSION_FILE = config.get('versionFile')
23
+
24
+ /**
25
+ * Return an absolute path to an installed quire-11ty version
26
+ *
27
+ * @return {String} path to installed quire-11ty version
28
+ */
29
+ function getPath(version=QUIRE_VERSION) {
30
+ const absolutePath = path.relative('/', path.join(INSTALL_PATH, version))
31
+ if (!fs.existsSync(absolutePath)) {
32
+ console.error(`[CLI:quire] quire-11ty@${version} is not installed`)
33
+ return null
34
+ }
35
+ console.debug(`[CLI:quire] %s`, absolutePath)
36
+ return absolutePath
37
+ }
38
+
39
+ /**
40
+ * Read the required quire-11ty version for the quire version file
41
+ *
42
+ * @param {String} projectPath Absolute system path to the project root
43
+ *
44
+ * @return {String} version Quire-11ty semantic version
45
+ */
46
+ function getVersion(projectPath) {
47
+ projectPath = projectPath || path.resolve(cwd())
48
+ const version = fs.readFileSync(path.join(projectPath, VERSION_FILE), { encoding:'utf8' })
49
+ const projectName = path.basename(projectPath)
50
+ console.debug(`${projectName} set to use quire-11ty@${version}`)
51
+ return version
52
+ }
53
+
54
+ /**
55
+ * Read required quire-11ty and starter versions from starter peerDependencies
56
+ *
57
+ * @param {String} projectPath Absolute system path to the project root
58
+ *
59
+ * @return {Object}
60
+ * @property {String} quire11tyVersion Latest compatible Quire-11ty semantic version
61
+ * @property {String} starterVersion Starter project version defined in the starter package.json
62
+ */
63
+ async function getVersionsFromStarter(projectPath) {
64
+ const projectPackageConfig = fs.readFileSync(path.join(projectPath, 'package.json'), { encoding:'utf8' })
65
+ const { peerDependencies, version: starterVersion } = JSON.parse(projectPackageConfig)
66
+ const quire11tyVersion = peerDependencies[PACKAGE_NAME]
67
+ return { quire11tyVersion, starterVersion }
68
+ }
69
+
70
+ /**
71
+ * Clone or copy a Quire starter project
72
+ *
73
+ * @param {String} starter A repository URL or an absolute path to local starter
74
+ * @TODO resolve both absolute and relative paths to local starter repositories
75
+ * @param {String} projectPath Absolute system path to the project root
76
+ *
77
+ * @return {String} quireVersion A string indicating the current version
78
+ * of quire being used with a new project
79
+ */
80
+ async function initStarter (starter, projectPath, options) {
81
+ projectPath = projectPath || cwd()
82
+
83
+ // ensure that the target path exists
84
+ fs.ensureDirSync(projectPath)
85
+
86
+ // if the target directory exists it must be empty
87
+ if (!isEmpty(projectPath)) {
88
+ const location = projectPath === '.' ? 'the current directory' : projectPath
89
+ console.error(`[CLI] cannot create a starter project in ${location} because it is not empty`)
90
+ // @TODO cleanup directories from failed new command
91
+ return
92
+ }
93
+
94
+ console.debug('[CLI:quire] init-starter',
95
+ `\n project: ${path.join(__dirname, projectPath)}`,
96
+ `\n starter: ${starter}`
97
+ )
98
+
99
+ /**
100
+ * Clone starter project repository
101
+ * @todo pipe `git clone` status to stdout for better UX
102
+ */
103
+ await git
104
+ .cwd(projectPath)
105
+ .clone(starter, '.')
106
+ .catch((error) => console.error('[CLI:error] ', error))
107
+
108
+ /**
109
+ * Determine the quire-11ty version to use in the new project,
110
+ * from the quireVersion option or as required by the starter project.
111
+ *
112
+ * Uses 'latest' to get the latest semantic version compatible with version ranges
113
+ */
114
+ const { quire11tyVersion, starterVersion } = await getVersionsFromStarter(projectPath)
115
+ const quireVersion = await latest(options.quireVersion || quire11tyVersion)
116
+ setVersion(projectPath, quireVersion)
117
+
118
+ /**
119
+ * Write quire-11ty, quire-cli, starter versions to the version file
120
+ */
121
+ const versionInfo = {
122
+ cli: packageConfig.version,
123
+ starter: `${starter}@${starterVersion}`,
124
+ }
125
+ fs.writeFileSync(path.join(projectPath, VERSION_FILE), JSON.stringify(versionInfo))
126
+
127
+ // Re-initialize project directory as a new git repository
128
+ await fs.remove(path.join(projectPath, '.git'))
129
+
130
+ /**
131
+ * Remove the starter repository package config file
132
+ * and create a new package config for the project
133
+ * (this is necessary for Eleventy to resolve project paths)
134
+ * @todo allow interactive init using a custom questionnaire
135
+ * @see https://docs.npmjs.com/creating-a-package-json-file#customizing-the-packagejson-questionnaire
136
+ */
137
+ await fs.remove(path.join(projectPath, 'package.json'))
138
+ await execaCommand('npm init --yes', { cwd: projectPath })
139
+
140
+ /**
141
+ * Create an initial commit of files in new repository
142
+ * @todo use a localized string for the commit message
143
+ */
144
+ const projectFiles = fs.readdirSync(projectPath)
145
+ await git.init().add(projectFiles).commit('Initial Commit')
146
+ return quireVersion
147
+ }
148
+
149
+ /**
150
+ * Install `quire-11ty` directly into a quire project
151
+ *
152
+ * @TODO refactor this to use the install method with pre and post-install hooks
153
+ * or steps to prepare the working directory and cleanup on error and completion
154
+ *
155
+ * @param {String} projectPath Absolute system path to the project root
156
+ * @param {Object} options options passed from `quire new` command
157
+ * @return {Promise}
158
+ */
159
+ async function installInProject(projectPath, quireVersion, options = {}) {
160
+ const { quirePath } = options
161
+ const quire11tyPackage = `${PACKAGE_NAME}@${quireVersion}`
162
+
163
+ console.debug(`[CLI:quire] installing ${quire11tyPackage} into ${projectPath}`)
164
+
165
+ /**
166
+ * Delete the starter project package configuration so that it can be replaced
167
+ * with the `@thegetty/quire-11ty` configuration
168
+ * @TODO If a user runs quire eject at a later date we may want to merge their
169
+ * package.json with the `quire-11ty` dev dependencies, scripts, etc
170
+ */
171
+ await git
172
+ .cwd(projectPath)
173
+ .rm(['package.json'])
174
+ .catch((error) => console.error('[CLI:error] ', error))
175
+
176
+ const temp11tyDirectory = '.temp'
177
+ const tempDir = path.join(projectPath,temp11tyDirectory)
178
+ fs.mkdirSync(tempDir)
179
+
180
+ // Copy if passed a path and it exists, otherwise attempt to download the tarball for this pathspec
181
+ if (fs.existsSync(quirePath)) {
182
+ fs.cpSync(quirePath, tempDir, {recursive: true})
183
+ } else {
184
+ await execaCommand(`npm pack ${ options.debug ? '--debug' : '--quiet' } --pack-destination ${tempDir} ${quire11tyPackage}`)
185
+
186
+ // Extract only the package dir from the tar bar and strip it from the extracted path
187
+ const tarballPath = path.join(tempDir, `thegetty-quire-11ty-${quireVersion}.tgz`)
188
+ await execaCommand(`tar -xzf ${tarballPath} -C ${tempDir} --strip-components=1 package/`)
189
+
190
+ fs.rmSync(tarballPath)
191
+ }
192
+
193
+ // Copy `.temp` to projectPath
194
+ fs.cpSync(tempDir, projectPath, {recursive: true})
195
+
196
+ console.debug('[CLI:quire] installing dev dependencies into quire project')
197
+ /**
198
+ * Manually install necessary dev dependencies to run 11ty;
199
+ * these must be `devDependencies` so that they are not bundled into
200
+ * the final `_site` package when running `quire build`
201
+ */
202
+ await execaCommand('npm cache clean --force', { cwd: projectPath })
203
+ try {
204
+ await execaCommand('npm install --save-dev', { cwd: projectPath })
205
+ } catch(error) {
206
+ console.warn(`[CLI:error]`, error)
207
+ fs.rmSync(projectPath, {recursive: true})
208
+ return
209
+ }
210
+
211
+ const eleventyFilesToCommit = fs
212
+ .readdirSync(tempDir)
213
+ .filter((filePath) => filePath !== 'node_modules')
214
+
215
+ eleventyFilesToCommit.push('package-lock.json')
216
+
217
+ /**
218
+ * Create an additional commit of new `@thegetty/quire-11ty` files in repository
219
+ * @todo use a localized string for the commit message
220
+ */
221
+ await git.add(eleventyFilesToCommit).commit('Adds `@thegetty/quire-11ty` files')
222
+
223
+ // remove temporary 11ty install directory
224
+ fs.rmSync(path.join(projectPath, temp11tyDirectory), {recursive: true})
225
+ }
226
+
227
+ /**
228
+ * Retrieve latest published version of the `quire-11ty` package
229
+ * or the latest compatible version with the provided semantic version string
230
+ *
231
+ * @param {String} version A semantic version string, i.e `^1.0.0-pre-release.0`
232
+ *
233
+ * @return {String} `quire-11ty@latest` semantic version string
234
+ */
235
+ async function latest(version) {
236
+ let quireVersion;
237
+ if (!version || version === 'latest') {
238
+ const { stdout } =
239
+ await execa('npm', ['view', PACKAGE_NAME, 'version'])
240
+ quireVersion = stdout
241
+ } else {
242
+ const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}`)
243
+ const json = await response.json()
244
+ const versions = Object.keys(json.versions)
245
+ quireVersion = semver.maxSatisfying(versions, version)
246
+ }
247
+ if (!quireVersion) {
248
+ throw new Error(`[CLI:quire] Sorry, we couldn't find a version of quire-11ty compatible with the version "${version}". You can set the quire-11ty version in the starter project's package.json or specify a version when running \`quire new\` with the \`--quire-version\` flag. You can run \`npm view @thegetty/quire-11ty versions\` to view all versions.`)
249
+ }
250
+ return quireVersion
251
+ }
252
+
253
+ /**
254
+ * List installed versions of the `quire-11ty` package
255
+ */
256
+ function list() {
257
+ return fs.readdirSync(INSTALL_PATH)
258
+ }
259
+
260
+ /**
261
+ * Removes the installed version of `quire-11ty`
262
+ *
263
+ * @param {String} `quire-11ty` semantic version
264
+ * @return {Promise}
265
+ */
266
+ async function remove(version) {
267
+ if (!version) {
268
+ console.error('No version specified.')
269
+ }
270
+ const dir = `${INSTALL_PATH}/${version}`
271
+ fs.rmdir(dir, { recursive: true }, (error) => {
272
+ if (error) {
273
+ throw error
274
+ }
275
+ console.info(`deleted ${dir}`)
276
+ })
277
+ symlinkLatest()
278
+ }
279
+
280
+ /**
281
+ * Sets the quire-11ty version for a project
282
+ *
283
+ * @param {String} version a version identifier or distribution tag
284
+ */
285
+ function setVersion(projectPath, version) {
286
+ const projectName = path.basename(projectPath)
287
+ console.info(`${projectName} set to use quire-11ty@${version}`)
288
+ }
289
+
290
+ /**
291
+ * Update symbolic link to the latest _installed_ version of `quire-11ty`
292
+ *
293
+ * @todo why does this not work using `fs-extra` `createSymlinkSync()`
294
+ */
295
+ function symlinkLatest() {
296
+ const latestInstalledVersion = fs
297
+ .readdirSync(path.join(__dirname, 'versions'))
298
+ .filter((dirent) => semver.valid(semver.coerce(dirent)))
299
+ .sort(semver.rcompare)[0]
300
+ const target = path.join(__dirname, 'versions', latestInstalledVersion)
301
+ const source = path.join(__dirname, 'versions', 'latest')
302
+ const type = IS_WINDOWS ? 'junction' : 'dir'
303
+
304
+ console.debug('[CLI:quire] symlinking latest installed version')
305
+
306
+ try {
307
+ return fs.symlinkSync(target, source, type)
308
+ } catch (error) {
309
+ switch (error.code) {
310
+ case 'EEXIST':
311
+ case 'EISDIR':
312
+ // if the target file already exists proceed...
313
+ break
314
+ case 'ENOENT':
315
+ try {
316
+ fs.mkdirSync(path.dirname(source))
317
+ } catch (mkdirError) {
318
+ mkdirError.message = `Error trying to symlink ${target} to ${source}`
319
+ throw mkdirError
320
+ }
321
+ // do we need to symlink in this case?
322
+ // return fs.symlinkSync(target, source, type)
323
+ break
324
+ default:
325
+ console.error('[CLI:quire] unable to symlink latest installed version')
326
+ throw error
327
+ }
328
+ }
329
+
330
+ fs.unlinkSync(source)
331
+ return fs.symlinkSync(target, source, type)
332
+ }
333
+
334
+ /**
335
+ * Get an array of published `quire-11ty` package versions
336
+ *
337
+ * @return {Array<String>} published versions
338
+ */
339
+ async function versions() {
340
+ return await execa('npm', ['show', PACKAGE_NAME, 'versions'])
341
+ }
342
+
343
+ export const quire = {
344
+ getPath,
345
+ getVersion,
346
+ initStarter,
347
+ installInProject,
348
+ latest,
349
+ list,
350
+ remove,
351
+ setVersion,
352
+ versions,
353
+ }