@thegetty/quire-cli 1.0.0-pre-release.14 → 1.0.0-pre-release.15
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/package.json +4 -1
- package/src/commands/build.js +2 -3
- package/src/commands/clean.js +2 -10
- package/src/commands/epub.js +67 -0
- package/src/commands/index.js +3 -6
- package/src/commands/pdf.js +70 -0
- package/src/commands/preview.js +4 -5
- package/src/helpers/clean.js +3 -1
- package/src/helpers/os-utils.js +20 -0
- package/src/lib/11ty/api.js +13 -3
- package/src/lib/11ty/cli.js +2 -2
- package/src/lib/11ty/paths.js +3 -2
- package/src/lib/epub/README.md +7 -0
- package/src/lib/epub/epub.js +9 -0
- package/src/lib/epub/index.js +36 -0
- package/src/lib/epub/pandoc.js +42 -0
- package/src/lib/pdf/README.md +12 -0
- package/src/lib/pdf/index.js +38 -0
- package/src/lib/pdf/paged.js +42 -0
- package/src/lib/pdf/prince.js +17 -0
- package/src/main.js +20 -12
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.
|
|
4
|
+
"version": "1.0.0-pre-release.15",
|
|
5
5
|
"author": "Getty Digital",
|
|
6
6
|
"license": "SEE LICENSE IN https://github.com/thegetty/quire/blob/main/LICENSE",
|
|
7
7
|
"bugs": {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"conf": "^10.2.0",
|
|
52
52
|
"cross-env": "^7.0.3",
|
|
53
53
|
"del": "^7.0.0",
|
|
54
|
+
"epubjs": "^0.4.2",
|
|
54
55
|
"execa": "^6.1.0",
|
|
55
56
|
"fs-extra": "^11.0.0",
|
|
56
57
|
"hosted-git-info": "^6.1.1",
|
|
@@ -58,7 +59,9 @@
|
|
|
58
59
|
"inquirer": "^9.1.4",
|
|
59
60
|
"install-npm-version": "^1.0.8",
|
|
60
61
|
"loglevel": "^1.8.0",
|
|
62
|
+
"open": "^8.4.0",
|
|
61
63
|
"ora": "^6.1.2",
|
|
64
|
+
"pagedjs-cli": "^0.3.1",
|
|
62
65
|
"semver": "^7.3.8",
|
|
63
66
|
"simple-git": "^3.15.0",
|
|
64
67
|
"update-notifier": "^6.0.2"
|
package/src/commands/build.js
CHANGED
|
@@ -19,10 +19,9 @@ export default class BuildCommand extends Command {
|
|
|
19
19
|
version: '1.0.0',
|
|
20
20
|
args: [
|
|
21
21
|
// [
|
|
22
|
-
// '[formats]', 'output formats',
|
|
22
|
+
// '[formats...]', 'output formats',
|
|
23
23
|
// {
|
|
24
|
-
// choices: ['
|
|
25
|
-
// default: ['html', 'html only']
|
|
24
|
+
// choices: ['pdf', 'epub'],
|
|
26
25
|
// }
|
|
27
26
|
// ],
|
|
28
27
|
],
|
package/src/commands/clean.js
CHANGED
|
@@ -20,15 +20,7 @@ export default class CleanCommand extends Command {
|
|
|
20
20
|
description: 'Remove build outputs',
|
|
21
21
|
summary: 'remove build outputs',
|
|
22
22
|
version: '1.0.0',
|
|
23
|
-
args: [
|
|
24
|
-
// [
|
|
25
|
-
// '[formats]', 'output formats',
|
|
26
|
-
// {
|
|
27
|
-
// choices: ['html', 'pdf', 'epub'],
|
|
28
|
-
// default: ['html', 'html only']
|
|
29
|
-
// }
|
|
30
|
-
// ],
|
|
31
|
-
],
|
|
23
|
+
args: [],
|
|
32
24
|
options: [
|
|
33
25
|
[ '-d', '--dry-run', 'show paths to be cleaned without deleting files' ],
|
|
34
26
|
[ '-p', '--progress', 'display progress of removing files' ],
|
|
@@ -52,7 +44,7 @@ export default class CleanCommand extends Command {
|
|
|
52
44
|
? `the following files ${options.dryRun ? 'will be' : 'have been'} deleted:`
|
|
53
45
|
: 'no files to delete'
|
|
54
46
|
|
|
55
|
-
console.debug(`[CLI] ${message}
|
|
47
|
+
console.debug(`[CLI] ${message}\n${deletedPaths.join('\n')}`)
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
preAction(command) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Command from '#src/Command.js'
|
|
2
|
+
import { paths, projectRoot } from '#lib/11ty/index.js'
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import libEpub from '#lib/epub/index.js'
|
|
5
|
+
import open from 'open'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Quire CLI `build epub` Command
|
|
10
|
+
*
|
|
11
|
+
* Generate EPUB from Eleventy `build` output.
|
|
12
|
+
*
|
|
13
|
+
* @class EpubCommand
|
|
14
|
+
* @extends {Command}
|
|
15
|
+
*/
|
|
16
|
+
export default class EpubCommand extends Command {
|
|
17
|
+
static definition = {
|
|
18
|
+
name: 'epub',
|
|
19
|
+
description: 'Generate publication EPUB',
|
|
20
|
+
summary: 'run build epub',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
args: [],
|
|
23
|
+
options: [
|
|
24
|
+
[
|
|
25
|
+
'--lib <module>', 'use the specified epub library', 'epubjs',
|
|
26
|
+
{ choices: ['epubjs', 'pandoc'], default: 'epubjs' }
|
|
27
|
+
],
|
|
28
|
+
[ '--open', 'open EPUB in default application' ],
|
|
29
|
+
[ '--debug', 'run epub with debug output' ],
|
|
30
|
+
],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
super(EpubCommand.definition)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async action(options, command) {
|
|
38
|
+
if (options.debug) {
|
|
39
|
+
console.debug('[CLI] Command \'%s\' called with options %o', this.name(), options)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const input = path.join(projectRoot, paths.epub)
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(input)) {
|
|
45
|
+
console.error(`Unable to find Epub input at ${input}\nPlease first run the 'quire build' command.`)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const output = path.join(projectRoot, `${options.lib}.epub`)
|
|
50
|
+
|
|
51
|
+
const epubLib = await libEpub(options.lib, { ...options.debug })
|
|
52
|
+
await epubLib(input, output, { ...options.debug })
|
|
53
|
+
|
|
54
|
+
if (fs.existsSync(output) && options.open) open(output)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* test if build site has already be run and output can be reused
|
|
59
|
+
* @todo
|
|
60
|
+
*/
|
|
61
|
+
preAction(command) {
|
|
62
|
+
const options = command.opts()
|
|
63
|
+
if (options.debug) {
|
|
64
|
+
console.debug('[CLI] Calling \'epub\' command pre-action with options', options)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/commands/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { fileURLToPath
|
|
1
|
+
import { dynamicImport } from '#helpers/os-utils.js'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import fs from 'fs-extra'
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
|
|
@@ -15,10 +15,7 @@ const thisfile = path.basename(__filename)
|
|
|
15
15
|
const commands = await Promise.all(
|
|
16
16
|
fs.readdirSync(__dirname)
|
|
17
17
|
.filter((file) => file !== thisfile && file.match(/^.*\.js$/))
|
|
18
|
-
.map((file) =>
|
|
19
|
-
? import(pathToFileURL(path.resolve(__dirname, file)))
|
|
20
|
-
: import(path.resolve(__dirname, file))
|
|
21
|
-
)
|
|
18
|
+
.map(async (file) => await dynamicImport(path.resolve(__dirname, file)))
|
|
22
19
|
).then((modules) => {
|
|
23
20
|
return modules.map(({ default: CommandClass }) => new CommandClass())
|
|
24
21
|
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Command from '#src/Command.js'
|
|
2
|
+
import { paths, projectRoot } from '#lib/11ty/index.js'
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import libPdf from '#lib/pdf/index.js'
|
|
5
|
+
import open from 'open'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
// import testcwd from '#helpers/test-cwd.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Quire CLI `pdf` Command
|
|
11
|
+
*
|
|
12
|
+
* Generate PDF from Eleventy `build` output.
|
|
13
|
+
*
|
|
14
|
+
* @class PDFCommand
|
|
15
|
+
* @extends {Command}
|
|
16
|
+
*/
|
|
17
|
+
export default class PDFCommand extends Command {
|
|
18
|
+
static definition = {
|
|
19
|
+
name: 'pdf',
|
|
20
|
+
description: 'Generate publication PDF',
|
|
21
|
+
summary: 'run build pdf',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
args: [
|
|
24
|
+
],
|
|
25
|
+
options: [
|
|
26
|
+
[
|
|
27
|
+
'--lib <module>', 'use the specified pdf module', 'pagedjs',
|
|
28
|
+
{ choices: ['pagedjs', 'prince'], default: 'pagedjs' }
|
|
29
|
+
],
|
|
30
|
+
[ '--open', 'open PDF in default application' ],
|
|
31
|
+
[ '--debug', 'run build with debug output to console' ],
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
super(PDFCommand.definition)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async action(options, command) {
|
|
40
|
+
if (options.debug) {
|
|
41
|
+
console.debug('[CLI] Command \'%s\' called with options %o', this.name(), options)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const input = path.join(projectRoot, paths.output, 'pdf.html')
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(input)) {
|
|
47
|
+
console.error(`Unable to find PDF input at ${input}\nPlease first run the 'quire build' command.`)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const output = path.join(projectRoot, `${options.lib}.pdf`)
|
|
52
|
+
|
|
53
|
+
const pdfLib = await libPdf(options.lib, { ...options.debug })
|
|
54
|
+
await pdfLib(input, output, { ...options.debug })
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
if (fs.existsSync(output) && options.open) open(output)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(error)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @todo test if build has already be run and output can be reused
|
|
66
|
+
*/
|
|
67
|
+
preAction(command) {
|
|
68
|
+
// testcwd(command)
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/commands/preview.js
CHANGED
|
@@ -18,10 +18,9 @@ export default class PreviewCommand extends Command {
|
|
|
18
18
|
version: '1.0.0',
|
|
19
19
|
args: [
|
|
20
20
|
// [
|
|
21
|
-
// '[formats]', 'output formats',
|
|
21
|
+
// '[formats...]', 'output formats',
|
|
22
22
|
// {
|
|
23
|
-
// choices: ['
|
|
24
|
-
// default: ['html', 'html only']
|
|
23
|
+
// choices: ['pdf', 'epub'],
|
|
25
24
|
// }
|
|
26
25
|
// ],
|
|
27
26
|
],
|
|
@@ -41,9 +40,9 @@ export default class PreviewCommand extends Command {
|
|
|
41
40
|
super(PreviewCommand.definition)
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
action(options
|
|
43
|
+
async action(args, options, command) {
|
|
45
44
|
if (options.debug) {
|
|
46
|
-
console.
|
|
45
|
+
console.debug('[CLI] Command \'%s\' called with arguments [%o] and options %o', this.name(), args.join(', '), options)
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
if (options['11ty'] === 'cli') {
|
package/src/helpers/clean.js
CHANGED
|
@@ -9,8 +9,10 @@ import path from 'node:path'
|
|
|
9
9
|
*/
|
|
10
10
|
export async function clean (projectRoot, paths, options = {}) {
|
|
11
11
|
const pathsToClean = [
|
|
12
|
+
path.join(projectRoot, paths.epub),
|
|
12
13
|
path.join(projectRoot, paths.output),
|
|
13
|
-
path.join(projectRoot,
|
|
14
|
+
path.join(projectRoot, '*.epub'),
|
|
15
|
+
path.join(projectRoot, '*.pdf'),
|
|
14
16
|
]
|
|
15
17
|
|
|
16
18
|
/**
|
package/src/helpers/os-utils.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Determine if the process is running on Windows operating system
|
|
3
5
|
*/
|
|
4
6
|
export const IS_WINDOWS =
|
|
5
7
|
process.platform === 'win32' || /^(cygwin|msys)$/.test(process.env.OSTYPE)
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Cross platform dynamic module import
|
|
11
|
+
* On the Windows platform ESM modules are loaded using the file:// scheme.
|
|
12
|
+
*
|
|
13
|
+
* Nota bene: a relative `path` argument is relative to this module!
|
|
14
|
+
*
|
|
15
|
+
*
|
|
16
|
+
* @param {String} path An aboslute path for the module to import
|
|
17
|
+
* @return {Object} Imported module
|
|
18
|
+
*/
|
|
19
|
+
export const dynamicImport = async (path) => {
|
|
20
|
+
try {
|
|
21
|
+
return IS_WINDOWS ? await import(pathToFileURL(path)) : await import(path)
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`[CLI] unable to import ${path}`, error)
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/lib/11ty/api.js
CHANGED
|
@@ -32,7 +32,7 @@ const factory = async (options = {}) => {
|
|
|
32
32
|
*/
|
|
33
33
|
const copyOptions = {
|
|
34
34
|
debug: options.debug || false,
|
|
35
|
-
expand:
|
|
35
|
+
expand: false,
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -83,11 +83,19 @@ const factory = async (options = {}) => {
|
|
|
83
83
|
return addPassthroughCopy(entry, copyOptions)
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Event callback when a build completes
|
|
89
|
+
* @see https://www.11ty.dev/docs/events/#eleventy.after
|
|
90
|
+
*/
|
|
91
|
+
eleventyConfig.on('eleventy.after', async () => {
|
|
92
|
+
console.debug('[11ty:API] build complete')
|
|
93
|
+
})
|
|
94
|
+
|
|
86
95
|
return eleventyConfig
|
|
87
96
|
},
|
|
88
97
|
configPath: options.config || config,
|
|
89
98
|
quietMode: options.quiet || false,
|
|
90
|
-
// source: 'script',
|
|
91
99
|
})
|
|
92
100
|
|
|
93
101
|
return eleventy
|
|
@@ -111,9 +119,11 @@ export default {
|
|
|
111
119
|
|
|
112
120
|
eleventy.setDryRun(options.dryrun)
|
|
113
121
|
|
|
114
|
-
await eleventy.
|
|
122
|
+
await eleventy.write()
|
|
115
123
|
},
|
|
116
124
|
serve: async (options = {}) => {
|
|
125
|
+
process.cwd(projectRoot)
|
|
126
|
+
|
|
117
127
|
console.info('[CLI:11ty] running development server')
|
|
118
128
|
|
|
119
129
|
process.env.ELEVENTY_ENV = 'development'
|
package/src/lib/11ty/cli.js
CHANGED
|
@@ -82,10 +82,10 @@ export default {
|
|
|
82
82
|
|
|
83
83
|
command.push('--serve')
|
|
84
84
|
|
|
85
|
-
env.ELEVENTY_ENV = 'development'
|
|
86
|
-
|
|
87
85
|
if (options.port) command.push(`--port=${options.port}`)
|
|
88
86
|
|
|
87
|
+
env.ELEVENTY_ENV = 'development'
|
|
88
|
+
|
|
89
89
|
await execa('node', command, {
|
|
90
90
|
all: true,
|
|
91
91
|
cwd: projectRoot,
|
package/src/lib/11ty/paths.js
CHANGED
|
@@ -25,8 +25,6 @@ const version = 'latest'
|
|
|
25
25
|
|
|
26
26
|
export const projectRoot = process.cwd()
|
|
27
27
|
|
|
28
|
-
const inputDir = path.join(projectRoot, 'content')
|
|
29
|
-
|
|
30
28
|
/**
|
|
31
29
|
* Absolute path to the latest installed version of `quire-11ty`
|
|
32
30
|
* @todo use version read from the project `.quire-version` file
|
|
@@ -82,8 +80,11 @@ const getEleventyRoot = () => {
|
|
|
82
80
|
|
|
83
81
|
export const eleventyRoot = getEleventyRoot()
|
|
84
82
|
|
|
83
|
+
const inputDir = path.join(projectRoot, 'content')
|
|
84
|
+
|
|
85
85
|
export default {
|
|
86
86
|
config: path.join(eleventyRoot, '.eleventy.js'),
|
|
87
|
+
epub: path.relative(eleventyRoot, path.join(projectRoot, '_epub')),
|
|
87
88
|
input: path.relative(eleventyRoot, inputDir),
|
|
88
89
|
output: path.relative(eleventyRoot, path.join(projectRoot, '_site')),
|
|
89
90
|
data: path.relative(inputDir, path.join(eleventyRoot, '_computed')),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## CLI lib/EPUB Module
|
|
2
|
+
|
|
3
|
+
This module implements a façade for EPUB e-book file format generation libries. The module exports a single method that accepts a `lib` option and delegates to a façade for the specified EPUB library.
|
|
4
|
+
|
|
5
|
+
### Epub.js Façade
|
|
6
|
+
|
|
7
|
+
See the [`Epub.js` documentation](https://github.com/futurepress/epub.js).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// import epub from 'epubjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A façade module for the EPUB.js library
|
|
5
|
+
* @see https://github.com/futurepress/epub.js
|
|
6
|
+
*/
|
|
7
|
+
export default async (input, output, options) => {
|
|
8
|
+
console.info('[CLI:lib/epubjs] integration with epub.js is not yet implemented.')
|
|
9
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { dynamicImport } from '#helpers/os-utils.js'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
6
|
+
const __dirname = path.dirname(__filename)
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A façade delegation module for EPUB libraries
|
|
10
|
+
*/
|
|
11
|
+
export default async (lib = 'epubjs', options = {}) => {
|
|
12
|
+
let libName, libPath
|
|
13
|
+
|
|
14
|
+
switch (lib.toLowerCase()) {
|
|
15
|
+
case 'epubjs': {
|
|
16
|
+
libName = 'Epub.js'
|
|
17
|
+
libPath = path.join(__dirname, 'epub.js')
|
|
18
|
+
break
|
|
19
|
+
}
|
|
20
|
+
case 'pandoc': {
|
|
21
|
+
libName = 'Pandoc'
|
|
22
|
+
libPath = path.join(__dirname, 'pandoc.js')
|
|
23
|
+
break
|
|
24
|
+
}
|
|
25
|
+
default:
|
|
26
|
+
console.error(`[CLI:lib/pdf] Unrecognized EPUB library '${lib}'`)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { default: epubLib } = await dynamicImport(libPath)
|
|
31
|
+
|
|
32
|
+
return async (input, output, options = {}) => {
|
|
33
|
+
console.info(`[CLI:lib/epub] generating EPUB using ${libName}`)
|
|
34
|
+
return await epubLib(input, output, options)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import paths, { projectRoot } from '#lib/11ty/paths.js'
|
|
5
|
+
import which from '#helpers/which.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Filter directory entries for XHTML files
|
|
9
|
+
*
|
|
10
|
+
* @param {fs.Dirent} dirent directory entry
|
|
11
|
+
* @return {fs.Dirent} XHTML entries
|
|
12
|
+
*/
|
|
13
|
+
const xhtmlFiles = (dirent) => {
|
|
14
|
+
const stats = fs.lstatSync(dirent)
|
|
15
|
+
const { ext } = path.parse(dirent)
|
|
16
|
+
return stats.isFile() && ext === '.xhtml'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A façade module for interacting with Pandoc CLI.
|
|
21
|
+
* @see https://pandoc.org/MANUAL.html#general-options
|
|
22
|
+
*/
|
|
23
|
+
export default async (input, output, options) => {
|
|
24
|
+
which('pandoc')
|
|
25
|
+
|
|
26
|
+
const inputs = fs.readdirSync(input)
|
|
27
|
+
.map((entry) => path.join(input, entry))
|
|
28
|
+
.filter(xhtmlFiles)
|
|
29
|
+
|
|
30
|
+
const cmdOptions = [
|
|
31
|
+
`--from=html-native_divs+native_spans`,
|
|
32
|
+
`--to=epub`,
|
|
33
|
+
`--output=${output}`,
|
|
34
|
+
// `--epub-metadata=${path.join(input, 'dc.xml')}`,
|
|
35
|
+
// `--epub-cover-image=${coverImage}`,
|
|
36
|
+
// `--template=${path.join(input, 'template.xhtml')}`,
|
|
37
|
+
`--css=${path.join(input, '_assets', 'epub.css')}`,
|
|
38
|
+
`--standalone`,
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
await execa('pandoc', [...cmdOptions, ...inputs])
|
|
42
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## CLI lib/PDF Module
|
|
2
|
+
|
|
3
|
+
This module implements a façade for PDF generation libries.
|
|
4
|
+
The module exports a single method that accepts a `lib` option and delegates to a façade for the specified PDF library.
|
|
5
|
+
|
|
6
|
+
### Paged.js Façade
|
|
7
|
+
|
|
8
|
+
See the [`Paged.js` documentation](https://gitlab.coko.foundation/pagedjs/).
|
|
9
|
+
|
|
10
|
+
### Prince XML Façade
|
|
11
|
+
|
|
12
|
+
See the [Prince Command-line Reference](https://www.princexml.com/doc/command-line/).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { dynamicImport } from '#helpers/os-utils.js'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
6
|
+
const __dirname = path.dirname(__filename)
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A façade delegation module for PDF generation libraries
|
|
10
|
+
*/
|
|
11
|
+
export default async (lib = 'pagedjs', options = {}) => {
|
|
12
|
+
let libName, libPath
|
|
13
|
+
|
|
14
|
+
switch (lib.toLowerCase()) {
|
|
15
|
+
case 'paged':
|
|
16
|
+
case 'pagedjs': {
|
|
17
|
+
libName = 'Paged.js'
|
|
18
|
+
libPath = path.join(__dirname, 'paged.js')
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
case 'prince':
|
|
22
|
+
case 'princexml': {
|
|
23
|
+
libName = 'Prince'
|
|
24
|
+
libPath = path.join(__dirname, 'prince.js')
|
|
25
|
+
break
|
|
26
|
+
}
|
|
27
|
+
default:
|
|
28
|
+
console.error(`[CLI:lib/pdf] Unrecognized PDF library '${lib}'`)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { default: pdfLib } = await dynamicImport(libPath)
|
|
33
|
+
|
|
34
|
+
return async (input, output, options = {}) => {
|
|
35
|
+
console.info(`[CLI:lib/pdf] generating PDF using ${libName}`)
|
|
36
|
+
return await pdfLib(input, output, options)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Printer from 'pagedjs-cli'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A façade module for interacting with Paged.js
|
|
6
|
+
* @see https://gitlab.coko.foundation/pagedjs/
|
|
7
|
+
*/
|
|
8
|
+
export default async (input, output, options = {}) => {
|
|
9
|
+
/**
|
|
10
|
+
* Configure the Paged.js Printer options
|
|
11
|
+
* @see https://gitlab.coko.foundation/pagedjs/pagedjs-cli/-/blob/main/src/cli.js
|
|
12
|
+
*/
|
|
13
|
+
const printerOptions = {
|
|
14
|
+
allowLocal: true,
|
|
15
|
+
debug: options.debug || false,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const printer = new Printer(printerOptions)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Configure the Paged.js PDF options
|
|
22
|
+
* @see
|
|
23
|
+
*
|
|
24
|
+
* @todo verify options before setting them
|
|
25
|
+
* @todo catch errors thrown by the printer
|
|
26
|
+
*/
|
|
27
|
+
const pdfOptions = {
|
|
28
|
+
outlineTags: options.outlineTags || ['h1'],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
console.info(`[CLI:lib/pdf/pagedjs] printing ${input}`)
|
|
33
|
+
const file = await printer.pdf(input, pdfOptions)
|
|
34
|
+
.catch((error) => console.error(error))
|
|
35
|
+
|
|
36
|
+
if (file && output) {
|
|
37
|
+
fs.writeFile(output, file, (error) => { if (error) throw error })
|
|
38
|
+
}
|
|
39
|
+
} catch (ERR_FILE_NOT_FOUND) {
|
|
40
|
+
console.error(`[CLI:lib/pdf/pagedjs] file not found ${input}`)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { execa } from 'execa'
|
|
2
|
+
import which from '#helpers/which.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A façade module for interacting with the Prince CLI.
|
|
6
|
+
* @see https://www.princexml.com/doc/command-line/
|
|
7
|
+
*/
|
|
8
|
+
export default async (input, output, options = {}) => {
|
|
9
|
+
which('prince')
|
|
10
|
+
|
|
11
|
+
const cmdOptions = [
|
|
12
|
+
`--output=${output}`,
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const { stderror, stdout } = await execa('prince', [...cmdOptions, input])
|
|
16
|
+
return { stderror, stdout }
|
|
17
|
+
}
|
package/src/main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Command } from 'commander'
|
|
1
|
+
import { Argument, Command, Option } from 'commander'
|
|
2
2
|
import commands from './commands/index.js'
|
|
3
3
|
import packageConfig from '../package.json' assert { type: 'json' }
|
|
4
4
|
|
|
@@ -37,9 +37,15 @@ commands.forEach((command) => {
|
|
|
37
37
|
aliases.forEach((alias) => subCommand.alias(alias))
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @see https://github.com/tj/commander.js#more-configuration-1
|
|
42
|
+
*/
|
|
40
43
|
if (Array.isArray(args)) {
|
|
41
|
-
args.forEach(([
|
|
42
|
-
|
|
44
|
+
args.forEach(([ name, description, configuration = {} ]) => {
|
|
45
|
+
const argument = new Argument(name, description)
|
|
46
|
+
if (configuration.choices) argument.choices(configuration.choices)
|
|
47
|
+
if (configuration.default) argument.default(configuration.default)
|
|
48
|
+
subCommand.addArgument(argument)
|
|
43
49
|
})
|
|
44
50
|
}
|
|
45
51
|
|
|
@@ -47,16 +53,16 @@ commands.forEach((command) => {
|
|
|
47
53
|
* @see https://github.com/tj/commander.js/#options
|
|
48
54
|
*/
|
|
49
55
|
if (Array.isArray(options)) {
|
|
50
|
-
options.forEach((
|
|
51
|
-
if (Array.isArray(
|
|
56
|
+
options.forEach((entry) => {
|
|
57
|
+
if (Array.isArray(entry)) {
|
|
52
58
|
// ensure we can join the first two attributes as the option name
|
|
53
59
|
// when only the short or the long flag is defined in the array
|
|
54
|
-
if (
|
|
55
|
-
if (
|
|
56
|
-
|
|
60
|
+
if (entry[0].startsWith('--')) entry.unshift('\u0020'.repeat(4))
|
|
61
|
+
if (entry[0].startsWith('-') && !entry[1].startsWith('--')) {
|
|
62
|
+
entry.splice(1, 0, '')
|
|
57
63
|
}
|
|
58
64
|
// assign attribute names to the array of option attributes
|
|
59
|
-
const [ short, long, description, defaultValue ] =
|
|
65
|
+
const [ short, long, description, defaultValue ] = entry
|
|
60
66
|
// join short and long flags as the option name attribute
|
|
61
67
|
const name = /^-\w/.test(short) && /^--\w/.test(long)
|
|
62
68
|
? [short, long].join(', ')
|
|
@@ -64,11 +70,13 @@ commands.forEach((command) => {
|
|
|
64
70
|
subCommand.option(name, description, defaultValue)
|
|
65
71
|
} else {
|
|
66
72
|
/**
|
|
67
|
-
* @todo allow
|
|
73
|
+
* @todo allow options to be defined by a configuration object
|
|
68
74
|
* @see https://github.com/tj/commander.js/#more-configuration
|
|
69
75
|
*/
|
|
70
|
-
// const option = new Option(name, description
|
|
71
|
-
//
|
|
76
|
+
// const option = new Option(name, description)
|
|
77
|
+
// for (const property of configuration) {
|
|
78
|
+
// option[property](configuration[property])
|
|
79
|
+
// }
|
|
72
80
|
// subCommand.addOption(option)
|
|
73
81
|
console.error('@TODO please use an array to define option attributes')
|
|
74
82
|
}
|