@thegetty/quire-cli 1.0.0-pre-release.3 → 1.0.0-pre-release.5

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/bin/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node --no-warnings
2
+
2
3
  import cli from '#src/main.js'
3
4
  import packageConfig from '#root/package.json' assert { type: 'json' }
4
5
  import updateNotifier from 'update-notifier'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thegetty/quire-cli",
3
3
  "description": "Quire command-line interface",
4
- "version": "1.0.0-pre-release.3",
4
+ "version": "1.0.0-pre-release.5",
5
5
  "author": "Getty Digital",
6
6
  "license": "J Paul Getty Trust",
7
7
  "bugs": {
File without changes
@@ -0,0 +1,54 @@
1
+ import Command from '#src/Command.js'
2
+ import { build } from '#lib/pdf/index.js'
3
+ import open from 'open'
4
+
5
+ /**
6
+ * Quire CLI `build epub` Command
7
+ *
8
+ * Generate an EPUB from Eleventy `build` output.
9
+ *
10
+ * @class EpubCommand
11
+ * @extends {Command}
12
+ */
13
+ export default class EpubCommand extends Command {
14
+ static definition = {
15
+ name: 'epub',
16
+ description: 'Generate publication EPUB',
17
+ summary: 'run build epub',
18
+ version: '1.0.0',
19
+ args: [
20
+ ],
21
+ options: [
22
+ [
23
+ '--lib <module>', 'use the specified pdf module', 'pagedjs',
24
+ // { choices: ['pagedjs', 'prince'], default: 'pagedjs' }
25
+ ],
26
+ [ '--open', 'open PDF in default application' ],
27
+ [ '--debug', 'run build with debug output to console' ],
28
+ ],
29
+ }
30
+
31
+ constructor() {
32
+ super(PDFCommand.definition)
33
+ }
34
+
35
+ action(options, command) {
36
+ if (options.debug) {
37
+ console.debug('[CLI] Command \'%s\' called with options %o', this.name(), options)
38
+ }
39
+ const pdf = await pdf.build()
40
+ if (options.open) await open(pdf)
41
+ }
42
+
43
+ /**
44
+ * test that `build site` has been run and output can be reused
45
+ * @todo
46
+ */
47
+ preAction(command) {
48
+ const options = command.opts()
49
+ if (options.debug) {
50
+ console.debug('[CLI] Calling \'build\' command pre-action with options', options)
51
+ }
52
+ // console.log(command.parent)
53
+ }
54
+ }
@@ -0,0 +1,54 @@
1
+ import Command from '#src/Command.js'
2
+ import { build } from '#lib/pdf/index.js'
3
+ import open from 'open'
4
+
5
+ /**
6
+ * Quire CLI `build pdf` Command
7
+ *
8
+ * Generate a PDF from Eleventy `build` output.
9
+ *
10
+ * @class PDFCommand
11
+ * @extends {Command}
12
+ */
13
+ export default class PDFCommand extends Command {
14
+ static definition = {
15
+ name: 'pdf',
16
+ description: 'Generate publication PDF',
17
+ summary: 'run build pdf',
18
+ version: '1.0.0',
19
+ args: [
20
+ ],
21
+ options: [
22
+ [
23
+ '--lib <module>', 'use the specified pdf module', 'pagedjs',
24
+ // { choices: ['pagedjs', 'prince'], default: 'pagedjs' }
25
+ ],
26
+ [ '--open', 'open PDF in default application' ],
27
+ [ '--debug', 'run build with debug output to console' ],
28
+ ],
29
+ }
30
+
31
+ constructor() {
32
+ super(PDFCommand.definition)
33
+ }
34
+
35
+ action(options, command) {
36
+ if (options.debug) {
37
+ console.debug('[CLI] Command \'%s\' called with options %o', this.name(), options)
38
+ }
39
+ const pdf = await pdf.build()
40
+ if (options.open) await open(pdf)
41
+ }
42
+
43
+ /**
44
+ * test that `build site` has been run and output can be reused
45
+ * @todo
46
+ */
47
+ preAction(command) {
48
+ const options = command.opts()
49
+ if (options.debug) {
50
+ console.debug('[CLI] Calling \'build\' command pre-action with options', options)
51
+ }
52
+ // console.log(command.parent)
53
+ }
54
+ }
@@ -0,0 +1,66 @@
1
+ import Command from '#src/Command.js'
2
+ import { api, cli, paths, projectRoot } from '#lib/11ty/index.js'
3
+ import { clean } from '#helpers/clean.js'
4
+
5
+ /**
6
+ * Quire CLI `build site` Command
7
+ *
8
+ * Runs the Eleventy `build` to generate static output.
9
+ *
10
+ * @class SiteCommand
11
+ * @extends {Command}
12
+ */
13
+ export default class SiteCommand extends Command {
14
+ static definition = {
15
+ name: 'site',
16
+ description: 'Generate publication outputs',
17
+ summary: 'run build site',
18
+ version: '1.0.0',
19
+ isDefault: true,
20
+ args: [
21
+ // [
22
+ // '[formats]', 'output formats',
23
+ // {
24
+ // choices: ['html', 'pdf', 'epub'],
25
+ // default: ['html', 'html only']
26
+ // }
27
+ // ],
28
+ ],
29
+ options: [
30
+ [ '-d', '--dry-run', 'run build without writing files' ],
31
+ [ '-q', '--quiet', 'run build with no console messages' ],
32
+ [ '-v', '--verbose', 'run build with verbose console messages' ],
33
+ [
34
+ '--11ty <module>', 'use the specified 11ty module', 'cli',
35
+ // { choices: ['api', 'cli'], default: 'cli' }
36
+ ],
37
+ [ '--debug', 'run build with debug output to console' ],
38
+ ],
39
+ }
40
+
41
+ constructor() {
42
+ super(SiteCommand.definition)
43
+ }
44
+
45
+ action(options, command) {
46
+ if (options.debug) {
47
+ console.debug('[CLI] Command \'%s\' called with options %o', this.name(), options)
48
+ }
49
+
50
+ if (options['11ty'] === 'cli') {
51
+ console.debug('[CLI] running eleventy using lib/11ty cli')
52
+ cli.build(options)
53
+ } else {
54
+ console.debug('[CLI] running eleventy using lib/11ty api')
55
+ api.build(options)
56
+ }
57
+ }
58
+
59
+ preAction(command) {
60
+ const options = command.opts()
61
+ if (options.debug) {
62
+ console.debug('[CLI] Calling \'build\' command pre-action with options', options)
63
+ }
64
+ clean(projectRoot, paths, options)
65
+ }
66
+ }
@@ -194,14 +194,6 @@ To reinstall a `quire-11ty` version run
194
194
  quire version install <version> --force
195
195
  ```
196
196
 
197
- #### `link`
198
-
199
- Link to a local `quire-11ty` package for development and testing.
200
-
201
- ```sh
202
- quire version link [path]
203
- ```
204
-
205
197
  #### `list`
206
198
 
207
199
  List installed versions of `quire-11ty`
@@ -218,20 +210,12 @@ Remove outdated versions of `quire-11ty`
218
210
  quire version prune
219
211
  ```
220
212
 
221
- #### `unlink`
222
-
223
- Unlink a local `quire-11ty` package
213
+ #### `remove` (alias `uninstall`)
224
214
 
225
215
  ```sh
226
- quire version unlink
216
+ quire version remove <version>
227
217
  ```
228
218
 
229
- #### `uninstall` (alias `remove`)
230
-
231
219
  ```sh
232
220
  quire version uninstall <version>
233
221
  ```
234
-
235
- ```sh
236
- quire version remove <version>
237
- ```
@@ -23,6 +23,7 @@ export default class CreateCommand extends Command {
23
23
  ],
24
24
  options: [
25
25
  [ '--debug', 'debug the `quire new` command' ],
26
+ [ '--eject', 'install quire-11ty into the project directory', 'true' ],
26
27
  ],
27
28
  }
28
29
 
@@ -38,7 +39,7 @@ export default class CreateCommand extends Command {
38
39
  */
39
40
  async action(projectPath, starter, options = {}) {
40
41
  if (options.debug) {
41
- console.info('Command \'%s\' called with options %o', this.name, options)
42
+ console.info('Command \'%s\' called with options %o', CreateCommand.name, options)
42
43
  }
43
44
 
44
45
  if (!projectPath && !starter) {
@@ -52,7 +53,14 @@ export default class CreateCommand extends Command {
52
53
  // `git clone starter path`
53
54
  } else {
54
55
  const version = await quire.initStarter(starter, projectPath)
55
- await quire.install(version)
56
+ // @TODO we will want to abstract the test for emptiness to prevent further install steps on error
57
+ if (!version) return
58
+
59
+ if (options.eject) {
60
+ await quire.installInProject(projectPath, version, options)
61
+ } else {
62
+ await quire.install(version, options)
63
+ }
56
64
  }
57
65
  }
58
66
  }
@@ -8,7 +8,7 @@ import paths, { eleventyRoot, projectRoot } from './paths.js'
8
8
  * @param {Object} options Eleventy configuration options
9
9
  * @return {Eleventy} A configured instance of Eleventy
10
10
  */
11
- const factory = async (options) => {
11
+ const factory = async (options = {}) => {
12
12
  const { config, input, output } = paths
13
13
 
14
14
  console.info(`[CLI:11ty] projectRoot ${projectRoot}`)
@@ -20,13 +20,61 @@ const factory = async (options) => {
20
20
  */
21
21
  const { default: Eleventy } = await import(path.join(eleventyRoot, 'node_modules', '@11ty', 'eleventy', 'src', 'Eleventy.js'))
22
22
 
23
- return new Eleventy(input, output, {
23
+ /**
24
+ * Set Eleventy passthrough copy options
25
+ * @see https://www.11ty.dev/docs/copy/#advanced-options
26
+ * @see https://github.com/timkendrick/recursive-copy
27
+ */
28
+ const copyOptions = {
29
+ debug: options.debug || false,
30
+ expand: true,
31
+ }
32
+
33
+ /**
34
+ * Get an instance of the runtime of eleventy.
35
+ * @see https://github.com/11ty/eleventy/blob/src/Eleventy.js
36
+ *
37
+ * @param {String} input Path from which to read content templates
38
+ * @param {String} output Path where rendered files will be written
39
+ * @param {Object} options Options are merged with the Eleventy UserConfig
40
+ * @param {Object} config An eleventy configurtion object
41
+ *
42
+ * @returns {module:11ty/eleventy/Eleventy~Eleventy}
43
+ */
44
+ const eleventy = new Eleventy(input, output, {
24
45
  config: (eleventyConfig) => {
25
- // custom configuration
46
+ /**
47
+ * Override addPassthroughCopy to use _absolute_ system paths.
48
+ * @see https://www.11ty.dev/docs/copy/#passthrough-file-copy
49
+ * Nota bene: Eleventy addPassthroughCopy assumes paths are _relative_
50
+ * to the `config` file however the quire-cli separates 11ty from the
51
+ * project directory (`input`) and needs to use absolute system paths.
52
+ */
53
+ const addPassthroughCopy = eleventyConfig.addPassthroughCopy.bind(eleventyConfig)
54
+ eleventyConfig.addPassthroughCopy = (entry) => {
55
+ if (typeof entry === 'string') {
56
+ const filePath = path.resolve(entry) //path.join(projectRoot, file)
57
+ // console.debug('[11ty:API] passthrough copy %s', filePath)
58
+ return addPassthroughCopy(filePath, copyOptions)
59
+ } else {
60
+ // console.debug('[11ty:API] passthrough copy %o', entry)
61
+ entry = Object.fromEntries(
62
+ Object.entries(entry).map(([ src, dest ]) => {
63
+ return [ path.join(eleventyRoot, src), path.resolve(dest) ]
64
+ })
65
+ )
66
+ // console.debug('[11ty:API] passthrough copy %o', entry)
67
+ return addPassthroughCopy(entry, copyOptions)
68
+ }
69
+ }
70
+ return eleventyConfig
26
71
  },
27
72
  configPath: options.config || config,
28
73
  quietMode: options.quiet || false,
74
+ // source: 'script',
29
75
  })
76
+
77
+ return eleventy
30
78
  }
31
79
 
32
80
  /**
@@ -44,6 +92,9 @@ export default {
44
92
  if (options.debug) process.env.DEBUG = 'Eleventy*'
45
93
 
46
94
  const eleventy = await factory(options)
95
+
96
+ eleventy.setDryRun(options.dryrun)
97
+
47
98
  await eleventy.executeBuild()
48
99
  },
49
100
  serve: async (options = {}) => {
@@ -52,6 +103,7 @@ export default {
52
103
  if (options.debug) process.env.DEBUG = 'Eleventy*'
53
104
 
54
105
  const eleventy = await factory(options)
55
- await eleventy.serve()
106
+
107
+ await eleventy.serve(options.port)
56
108
  }
57
109
  }
@@ -1,7 +1,6 @@
1
1
  import { execa } from 'execa'
2
- import { fileURLToPath } from 'node:url'
3
2
  import path from 'node:path'
4
- import paths, { projectRoot } from './paths.js'
3
+ import paths, { eleventyRoot, projectRoot } from './paths.js'
5
4
 
6
5
  /**
7
6
  * A factory function to configure an Eleventy CLI command
@@ -9,20 +8,31 @@ import paths, { projectRoot } from './paths.js'
9
8
  * @param {Object} options Eleventy configuration options
10
9
  * @return {Array} Eleventy CLI options
11
10
  */
12
- const factory = () => {
11
+ const factory = (options = {}) => {
13
12
  const { config, input, output } = paths
14
13
 
15
14
  console.info(`[CLI:11ty] projectRoot ${projectRoot}`)
16
15
 
17
16
  console.debug('[CLI:11ty] %o', paths)
18
17
 
19
- return [
20
- `@11ty/eleventy`,
18
+ /**
19
+ * Use the version of Eleventy installed to `lib/quire/versions`
20
+ */
21
+ const eleventy =
22
+ path.join(eleventyRoot, 'node_modules', '@11ty', 'eleventy', 'cmd.js')
23
+
24
+ const command = [
25
+ eleventy,
21
26
  `--config=${config}`,
22
27
  `--input=${input}`,
23
28
  `--output=${output}`,
24
29
  `--incremental`,
25
30
  ]
31
+
32
+ if (options.quiet) command.push('--quiet')
33
+ if (options.verbose) command.push('--verbose')
34
+
35
+ return command
26
36
  }
27
37
 
28
38
  /**
@@ -33,7 +43,7 @@ export default {
33
43
  build: async (options = {}) => {
34
44
  console.info('[CLI:11ty] running eleventy build')
35
45
 
36
- const eleventyOptions = factory()
46
+ const eleventyCommand = factory()
37
47
 
38
48
  /**
39
49
  * Set execa environment variables
@@ -42,10 +52,10 @@ export default {
42
52
  const execaEnv = {}
43
53
 
44
54
  if (options.debug) execaEnv.DEBUG = 'Eleventy*'
45
- if (options.dryRun) eleventyOptions.push('--dryrun')
46
- if (options.quiet) eleventyOptions.push('--quiet')
47
55
 
48
- await execa('npx', eleventyOptions, {
56
+ if (options.dryRun) eleventyCommand.push('--dryrun')
57
+
58
+ await execa('node', eleventyCommand, {
49
59
  all: true,
50
60
  cwd: projectRoot,
51
61
  env: execaEnv,
@@ -56,9 +66,9 @@ export default {
56
66
  serve: async (options = {}) => {
57
67
  console.info(`[CLI:11ty] running eleventy serve`)
58
68
 
59
- const eleventyOptions = factory()
69
+ const eleventyCommand = factory(options)
60
70
 
61
- eleventyOptions.push('--serve')
71
+ eleventyCommand.push('--serve')
62
72
 
63
73
  /**
64
74
  * Set execa environment variables
@@ -67,11 +77,10 @@ export default {
67
77
  const execaEnv = {}
68
78
 
69
79
  if (options.debug) execaEnv.DEBUG = 'Eleventy*'
70
- if (options.port) eleventyOptions.push(`--port=${options.port}`)
71
- if (options.quiet) eleventyOptions.push('--quiet')
72
- if (options.verbose) eleventyOptions.push('--verbose')
73
80
 
74
- await execa('npx', eleventyOptions, {
81
+ if (options.port) eleventyCommand.push(`--port=${options.port}`)
82
+
83
+ await execa('node', eleventyCommand, {
75
84
  all: true,
76
85
  cwd: projectRoot,
77
86
  env: execaEnv,
@@ -1,4 +1,5 @@
1
1
  import { fileURLToPath } from 'node:url'
2
+ import fs from 'fs-extra'
2
3
  import path from 'node:path'
3
4
 
4
5
  const __filename = fileURLToPath(import.meta.url)
@@ -19,13 +20,25 @@ const __dirname = path.dirname(__filename)
19
20
  * and correctly resolve the path to the target of the 'latest' symlink
20
21
  */
21
22
  const cliRoot = path.resolve(__dirname, path.join('..', '..'))
23
+ const eleventyConfig = '.eleventy.js'
22
24
  const version = 'latest'
23
25
 
24
- // Absolute path to the current version of quire-11ty
25
- export const eleventyRoot =
26
- path.resolve(__dirname, path.join(cliRoot, 'lib', 'quire', 'versions', version))
26
+ // Test project directory for an eleventy configuration file
27
+ const hasEleventyConfig = (dir) => {
28
+ try {
29
+ return fs.readdirSync(dir).includes(eleventyConfig)
30
+ } catch (error) {
31
+ throw new Error(`[CLI:11ty] Unable to read project directory for eleventy config ${error}`)
32
+ }
33
+ }
34
+
27
35
  export const projectRoot = process.cwd()
28
36
 
37
+ // Absolute path to the current version of quire-11ty
38
+ export const eleventyRoot = hasEleventyConfig(projectRoot)
39
+ ? projectRoot
40
+ : path.resolve(__dirname, path.join(cliRoot, 'lib', 'quire', 'versions', version))
41
+
29
42
  export default {
30
43
  config: path.join(eleventyRoot, '.eleventy.js'),
31
44
  input: path.relative(eleventyRoot, path.join(projectRoot, 'content')),
@@ -141,9 +141,8 @@ async function initStarter (starter, projectPath) {
141
141
  /**
142
142
  * Install a specific version of `quire-11ty`
143
143
  *
144
- * If a `version` argument is not given `latest` is assumed.
145
- *
146
144
  * @param {String} version Quire-11ty semantic version
145
+ * @param {Object} options options passed from `quire new` command
147
146
  * @return {Promise}
148
147
  */
149
148
  async function install(version, options={}) {
@@ -157,7 +156,7 @@ async function install(version, options={}) {
157
156
  * @see https://github.com/scott-lin/install-npm-version
158
157
  */
159
158
  const installOptions = {
160
- Destination: path.join('../', version),
159
+ Destination: path.join('..', version),
161
160
  Debug: false,
162
161
  Overwrite: options.force || options.overwrite || false,
163
162
  Verbosity: options.debug ? 'Debug' : 'Silent',
@@ -184,6 +183,82 @@ async function install(version, options={}) {
184
183
  await execaCommand('npm install --save-dev')
185
184
  }
186
185
 
186
+ /**
187
+ * Install a specific version of `quire-11ty` directly into a quire project
188
+ *
189
+ * @param {String} projectPath Absolute system path to the project root
190
+ * @param {String} version Quire-11ty semantic version
191
+ * @param {Object} options options passed from `quire new` command
192
+ * @return {Promise}
193
+ */
194
+ async function installInProject(projectPath, version, options={}) {
195
+ console.debug(`[CLI:quire] installing quire-11ty@${version} into ${projectPath}`)
196
+
197
+ /**
198
+ * delete `package.json` from starter project, as it will be replaced with
199
+ * `package.json` from `@thegetty/quire-11ty`
200
+ * @TODO If a user runs quire eject at a later date we may want to merge their
201
+ * package.json with the `quire-11ty` dev dependencies, scripts, etc
202
+ */
203
+ await git
204
+ .cwd(projectPath)
205
+ .rm(['package.json'])
206
+ .catch((error) => console.error('[CLI:error] ', error))
207
+
208
+ const temp11tyDirectory = '.temp'
209
+ /**
210
+ * `Destination` is relative to `node_modules` of the working-directory
211
+ * so we have included a relative path to parent directory in order to
212
+ * install versions to a different local path.
213
+ * @see https://github.com/scott-lin/install-npm-version
214
+ */
215
+ const installOptions = {
216
+ Destination: path.join('..', temp11tyDirectory),
217
+ Debug: false,
218
+ Overwrite: options.force || options.overwrite || false,
219
+ Verbosity: options.debug ? 'Debug' : 'Silent',
220
+ WorkingDirectory: projectPath
221
+ }
222
+ await inv.Install(`${PACKAGE_NAME}@${version}`, installOptions)
223
+
224
+ // delete empty `node_modules` directory that `install-npm-version` creates
225
+ const invNodeModulesDir = path.join(projectPath, 'node_modules')
226
+ if (fs.existsSync(invNodeModulesDir)) fs.rmdir(invNodeModulesDir)
227
+
228
+ // Copy all files installed in `.temp` to projectPath
229
+ fs.copySync(path.join(projectPath, '.temp'), projectPath)
230
+
231
+ console.debug('[CLI:quire] installing dev dependencies into quire project')
232
+ /**
233
+ * Manually install necessary dev dependencies to run 11ty;
234
+ * these must be `devDependencies` so that they are not bundled into
235
+ * the final `_site` package when running `quire build`
236
+ */
237
+ await execaCommand('npm cache clean --force', { cwd: projectPath })
238
+ try {
239
+ await execaCommand('npm install --save-dev', { cwd: projectPath })
240
+ } catch(error) {
241
+ console.warn(`[CLI:error]`, error)
242
+ fs.removeSync(projectPath)
243
+ return
244
+ }
245
+
246
+ const eleventyFilesToCommit = fs
247
+ .readdirSync(path.join(projectPath, temp11tyDirectory))
248
+ .filter((filePath) => filePath !== 'node_modules')
249
+
250
+ eleventyFilesToCommit.push('package-lock.json')
251
+
252
+ /**
253
+ * Create an additional commit of new `@thegetty/quire-11ty` files in repository
254
+ * @todo use a localized string for the commit message
255
+ */
256
+ await git.add(eleventyFilesToCommit).commit('Adds `@thegetty/quire-11ty` files')
257
+
258
+ // remove temporary 11ty install directory
259
+ fs.removeSync(path.join(projectPath, temp11tyDirectory))
260
+ }
261
+
187
262
  /**
188
263
  * Retrieve latest published version of the `quire-11ty` package
189
264
  * Nota bene: `npm view [<@scope>/]<name>[@<version>] version`
@@ -317,6 +392,7 @@ export const quire = {
317
392
  getVersion,
318
393
  initStarter,
319
394
  install,
395
+ installInProject,
320
396
  latest,
321
397
  list,
322
398
  remove,
package/src/main.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node --no-warnings
2
1
  import { Command } from 'commander'
3
2
  import commands from './commands/index.js'
4
3
  import packageConfig from '../package.json' assert { type: 'json' }