@thegetty/quire-cli 1.0.0-rc.44 → 1.0.0-rc.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) 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 +9 -25
  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/schemas/config.schema.json +0 -194
  56. package/schemas/figures.schema.json +0 -56
  57. package/schemas/layout.schema.json +0 -5
  58. package/schemas/objects.schema.json +0 -62
  59. package/schemas/publication.schema.json +0 -140
  60. package/schemas/references.schema.json +0 -29
  61. package/src/Command.spec.js +0 -99
  62. package/src/commands/build.spec.js +0 -113
  63. package/src/commands/build.test.js +0 -397
  64. package/src/commands/clean.spec.js +0 -118
  65. package/src/commands/clean.test.js +0 -382
  66. package/src/commands/config.js +0 -238
  67. package/src/commands/config.spec.js +0 -107
  68. package/src/commands/config.test.js +0 -845
  69. package/src/commands/create.spec.js +0 -112
  70. package/src/commands/create.test.js +0 -415
  71. package/src/commands/doctor.js +0 -251
  72. package/src/commands/doctor.spec.js +0 -115
  73. package/src/commands/doctor.test.js +0 -1409
  74. package/src/commands/epub.spec.js +0 -114
  75. package/src/commands/epub.test.js +0 -503
  76. package/src/commands/help.js +0 -60
  77. package/src/commands/help.test.js +0 -132
  78. package/src/commands/info.spec.js +0 -72
  79. package/src/commands/info.test.js +0 -512
  80. package/src/commands/pdf.spec.js +0 -114
  81. package/src/commands/pdf.test.js +0 -464
  82. package/src/commands/preview.spec.js +0 -97
  83. package/src/commands/preview.test.js +0 -250
  84. package/src/commands/use.js +0 -56
  85. package/src/commands/use.spec.js +0 -61
  86. package/src/commands/use.test.js +0 -280
  87. package/src/commands/validate.spec.js +0 -90
  88. package/src/commands/validate.test.js +0 -378
  89. package/src/commands/workflows.js +0 -70
  90. package/src/errors/build/build-failed-error.js +0 -19
  91. package/src/errors/build/config-field-missing-error.js +0 -20
  92. package/src/errors/build/config-file-not-found-error.js +0 -20
  93. package/src/errors/build/index.js +0 -11
  94. package/src/errors/help/help-topic-not-found-error.js +0 -30
  95. package/src/errors/help/index.js +0 -8
  96. package/src/errors/index.js +0 -58
  97. package/src/errors/input/index.js +0 -8
  98. package/src/errors/input/invalid-input-error.js +0 -21
  99. package/src/errors/install/dependency-install-error.js +0 -19
  100. package/src/errors/install/directory-not-empty-error.js +0 -25
  101. package/src/errors/install/index.js +0 -12
  102. package/src/errors/install/invalid-path-error.js +0 -31
  103. package/src/errors/install/invalid-starter-error.js +0 -27
  104. package/src/errors/install/version-not-found-error.js +0 -35
  105. package/src/errors/output/epub-generation-error.js +0 -19
  106. package/src/errors/output/index.js +0 -14
  107. package/src/errors/output/invalid-epub-library-error.js +0 -20
  108. package/src/errors/output/invalid-pdf-library-error.js +0 -20
  109. package/src/errors/output/missing-build-output-error.js +0 -21
  110. package/src/errors/output/pdf-generation-error.js +0 -24
  111. package/src/errors/output/tool-not-found-error.js +0 -37
  112. package/src/errors/project/index.js +0 -10
  113. package/src/errors/project/not-in-project-error.js +0 -20
  114. package/src/errors/project/project-create-error.js +0 -21
  115. package/src/errors/quire-error.js +0 -27
  116. package/src/errors/validation/index.js +0 -3
  117. package/src/errors/validation/unknown-config-key-error.js +0 -27
  118. package/src/errors/validation/unknown-config-operation-error.js +0 -24
  119. package/src/helpers/docs-url.js +0 -32
  120. package/src/helpers/pager.js +0 -59
  121. package/src/helpers/pager.test.js +0 -85
  122. package/src/helpers/suggest-similar.js +0 -124
  123. package/src/helpers/suggest-similar.test.js +0 -108
  124. package/src/helpers/test-cwd.test.js +0 -192
  125. package/src/lib/11ty/index.test.js +0 -655
  126. package/src/lib/commander/index.js +0 -103
  127. package/src/lib/commander/index.test.js +0 -86
  128. package/src/lib/commander/options.js +0 -237
  129. package/src/lib/commander/options.test.js +0 -205
  130. package/src/lib/conf/build-status.js +0 -103
  131. package/src/lib/conf/build-status.test.js +0 -247
  132. package/src/lib/conf/config.test.js +0 -281
  133. package/src/lib/conf/format.js +0 -60
  134. package/src/lib/conf/format.test.js +0 -106
  135. package/src/lib/conf/helpers.js +0 -91
  136. package/src/lib/conf/helpers.test.js +0 -136
  137. package/src/lib/conf/index.js +0 -22
  138. package/src/lib/constants.js +0 -28
  139. package/src/lib/doctor/README.md +0 -667
  140. package/src/lib/doctor/checks/environment/cli-version.js +0 -62
  141. package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
  142. package/src/lib/doctor/checks/environment/git-available.js +0 -77
  143. package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
  144. package/src/lib/doctor/checks/environment/index.js +0 -13
  145. package/src/lib/doctor/checks/environment/node-version.js +0 -66
  146. package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
  147. package/src/lib/doctor/checks/environment/npm-available.js +0 -66
  148. package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
  149. package/src/lib/doctor/checks/environment/os-info.js +0 -48
  150. package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
  151. package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
  152. package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
  153. package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
  154. package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
  155. package/src/lib/doctor/checks/outputs/index.js +0 -10
  156. package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
  157. package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
  158. package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
  159. package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
  160. package/src/lib/doctor/checks/project/data-files.js +0 -56
  161. package/src/lib/doctor/checks/project/data-files.test.js +0 -125
  162. package/src/lib/doctor/checks/project/dependencies.js +0 -53
  163. package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
  164. package/src/lib/doctor/checks/project/index.js +0 -11
  165. package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
  166. package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
  167. package/src/lib/doctor/checks/project/quire-project.js +0 -38
  168. package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
  169. package/src/lib/doctor/checks/tools/index.js +0 -10
  170. package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
  171. package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
  172. package/src/lib/doctor/checks/tools/prince-available.js +0 -81
  173. package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
  174. package/src/lib/doctor/constants.js +0 -39
  175. package/src/lib/doctor/formatDuration.js +0 -108
  176. package/src/lib/doctor/formatDuration.test.js +0 -76
  177. package/src/lib/doctor/formatters/human.js +0 -257
  178. package/src/lib/doctor/formatters/human.test.js +0 -463
  179. package/src/lib/doctor/formatters/index.js +0 -8
  180. package/src/lib/doctor/formatters/json.js +0 -78
  181. package/src/lib/doctor/formatters/json.test.js +0 -174
  182. package/src/lib/doctor/formatters/shared.js +0 -129
  183. package/src/lib/doctor/formatters/shared.test.js +0 -194
  184. package/src/lib/doctor/index.js +0 -271
  185. package/src/lib/doctor/index.test.js +0 -797
  186. package/src/lib/epub/engines.js +0 -46
  187. package/src/lib/epub/index.test.js +0 -518
  188. package/src/lib/epub/pandoc.test.js +0 -122
  189. package/src/lib/epub/schema.js +0 -21
  190. package/src/lib/error/README.md +0 -170
  191. package/src/lib/error/handler.js +0 -107
  192. package/src/lib/git/index.spec.js +0 -80
  193. package/src/lib/git/index.test.js +0 -453
  194. package/src/lib/help/frontmatter.js +0 -77
  195. package/src/lib/help/frontmatter.test.js +0 -139
  196. package/src/lib/help/index.js +0 -146
  197. package/src/lib/help/index.test.js +0 -188
  198. package/src/lib/help/topics/configuration.md +0 -77
  199. package/src/lib/help/topics/debugging.md +0 -69
  200. package/src/lib/help/topics/epub.md +0 -74
  201. package/src/lib/help/topics/pdf.md +0 -74
  202. package/src/lib/help/topics/publishing.md +0 -80
  203. package/src/lib/help/topics/workflows.md +0 -50
  204. package/src/lib/installer/index.js +0 -309
  205. package/src/lib/installer/index.spec.js +0 -83
  206. package/src/lib/installer/index.test.js +0 -545
  207. package/src/lib/logger/README.md +0 -424
  208. package/src/lib/logger/debug.js +0 -132
  209. package/src/lib/logger/debug.spec.js +0 -130
  210. package/src/lib/logger/debug.test.js +0 -128
  211. package/src/lib/logger/index.js +0 -228
  212. package/src/lib/logger/index.spec.js +0 -131
  213. package/src/lib/logger/index.test.js +0 -578
  214. package/src/lib/npm/README.md +0 -127
  215. package/src/lib/npm/index.js +0 -198
  216. package/src/lib/npm/index.spec.js +0 -60
  217. package/src/lib/npm/index.test.js +0 -355
  218. package/src/lib/pdf/engines.js +0 -46
  219. package/src/lib/pdf/index.test.js +0 -708
  220. package/src/lib/pdf/paged.test.js +0 -366
  221. package/src/lib/pdf/prince.test.js +0 -202
  222. package/src/lib/pdf/schema.js +0 -21
  223. package/src/lib/pdf/split.test.js +0 -445
  224. package/src/lib/platform.js +0 -95
  225. package/src/lib/process/manager.js +0 -110
  226. package/src/lib/process/manager.test.js +0 -55
  227. package/src/lib/project/build.js +0 -162
  228. package/src/lib/project/build.test.js +0 -275
  229. package/src/lib/project/config.js +0 -48
  230. package/src/lib/project/config.test.js +0 -134
  231. package/src/lib/project/detect.js +0 -47
  232. package/src/lib/project/detect.test.js +0 -157
  233. package/src/lib/project/index.js +0 -56
  234. package/src/lib/project/output-paths.js +0 -87
  235. package/src/lib/project/output-paths.test.js +0 -66
  236. package/src/lib/project/paths.js +0 -272
  237. package/src/lib/project/version.js +0 -110
  238. package/src/lib/project/version.test.js +0 -350
  239. package/src/lib/reporter/index.test.js +0 -862
  240. package/src/main.spec.js +0 -61
  241. package/src/main.test.js +0 -347
  242. package/src/validators/validate-data-files.js +0 -154
  243. 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
+ }