Repository: Mogztter/asciidoctor-pdf.js
Pull request #158 (2020-01-23 17:24:43)

Support input from stdin
This is perhaps not quite right but might be close to being right. It is at the limit of my javascript ability. I think the cleanup of temporary html/adoc files needs to be better but it does seem to work :wink:

related to #155 


Patch:
diff --git a/lib/cli.js b/lib/cli.js
index 01df794..e749d5b 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -9,11 +9,14 @@ class ProcessorEmitter extends EventEmitter {}
 const processorEmitter = new ProcessorEmitter()
 
 const converter = require('./converter.js')
+// we look forward to the CLI having Invoker.readFromStdin()
+// https://github.com/Mogztter/asciidoctor-pdf.js/pull/158#issuecomment-577788688
+const stdin = require('./stdin')
 
 async function convertFiles (files, argv, options, verbose, preview) {
   for (const file of files) {
     if (verbose) {
-      console.log(`converting file ${file}`)
+      console.log(`converting file ${file.contents ? '-' : file.path}`)
     }
     await converter.convert(processor, file, options, argv.timings, argv.watch, preview)
   }
@@ -87,8 +90,17 @@ class PdfInvoker extends Invoker {
     converter.registerTemplateConverter(processor, templates)
     Invoker.prepareProcessor(args, processor)
     const options = this.options.options
-    if (files && files.length > 0) {
-      await convertFiles(files, args, options, verbose, preview)
+    if (this.options.stdin) {
+      const fictiveInputFile = `${this.options.base_dir || this.options.doc_dir || process.cwd()}/asciidoctor-pdf-stdin.adoc`
+      stdin.read((data) => {
+          const fileObj = {path: fictiveInputFile,
+                           contents: data}
+          convertFiles([fileObj], args, options, verbose, preview)
+        })
+      return { exit: false }
+    } else if (files && files.length > 0) {
+      const fileObjs = files.map(file => new Object({ path: file }))
+      await convertFiles(fileObjs, args, options, verbose, preview)
       if (watch) {
         const watchFiles = files.map((file) => {
           const dirname = path.dirname(file)
diff --git a/lib/converter.js b/lib/converter.js
index e74b832..c7bdab8 100644
--- a/lib/converter.js
+++ b/lib/converter.js
@@ -30,15 +30,15 @@ function registerTemplateConverter (processor, templates) {
 }
 
 async function convert (processor, inputFile, options, timings, watch, preview) {
-  const tempFile = getTemporaryHtmlFile(inputFile, options)
+  const tempFile = getTemporaryHtmlFile(inputFile.path, options)
   let workingDir
   if (options.to_dir) {
     await mkdirs(options.to_dir)
     workingDir = options.to_dir
   } else {
-    workingDir = path.dirname(inputFile)
+    workingDir = path.dirname(inputFile.path)
   }
-  const inputFilenameWithoutExt = path.basename(inputFile, path.extname(inputFile))
+  const inputFilenameWithoutExt = path.basename(inputFile.path, path.extname(inputFile.path))
   let outputFile = path.join(workingDir, inputFilenameWithoutExt + '.pdf')
   let outputToStdout = false
   if (options.to_file) {
@@ -55,14 +55,25 @@ async function convert (processor, inputFile, options, timings, watch, preview)
   }
   const instanceOptions = Object.assign({}, options, { to_file: tempFile })
   let doc
+  let timer
+
   if (timings) {
-    const timings = processor.Timings.$new()
-    instanceOptions.timings = timings
-    doc = processor.convertFile(inputFile, instanceOptions)
-    timings.$print_report(Opal.gvars.stderr, inputFile)
+    timer = processor.Timings.create()
+    instanceOptions.timings = timer
+  }
+
+  if ( inputFile.contents ) {
+    // data from stdin
+    doc = processor.convert(inputFile.contents, instanceOptions)
   } else {
-    doc = processor.convertFile(inputFile, instanceOptions)
+    doc = processor.convertFile(inputFile.path, instanceOptions)
   }
+
+  // if (typeof timer !== 'undefined' && timer !== null) {
+  if (timings) {
+    timer.printReport(process.stderr, inputFile.contents ? '-' : inputFile.path)
+  }
+
   const puppeteerConfig = {
     headless: !preview,
     args: ['--no-sandbox', '--allow-file-access-from-files']
diff --git a/lib/stdin.js b/lib/stdin.js
new file mode 100644
index 0000000..cb3a52c
--- /dev/null
+++ b/lib/stdin.js
@@ -0,0 +1,21 @@
+const readFromStdin = (callback) => {
+  const encoding = 'utf-8'
+  let data
+  data = ''
+  process.stdin.setEncoding(encoding)
+  process.stdin.on('readable', function () {
+    const chunk = process.stdin.read()
+    if (chunk !== null) {
+      data += chunk
+    }
+  })
+  process.stdin.on('end', function () {
+    // There will be a trailing \n from the user hitting enter. Get rid of it.
+    data = data.replace(/\n$/, '')
+    callback(data)
+  })
+}
+
+module.exports = {
+  read: readFromStdin
+}


Test patch:
diff --git a/test/pdf_test.js b/test/pdf_test.js
index ddb8e75..3e5a743 100644
--- a/test/pdf_test.js
+++ b/test/pdf_test.js
@@ -61,7 +61,7 @@ describe('PDF converter', function () {
   const convert = async (inputFile, outputFile, options) => {
     const opts = options || {}
     opts.to_file = outputFile
-    await converter.convert(asciidoctor, inputFile, opts, false)
+    await converter.convert(asciidoctor, { path: inputFile }, opts, false)
     return PDFDocument.load(fs.readFileSync(outputFile))
   }
 
@@ -113,7 +113,7 @@ describe('PDF converter', function () {
     const outputFile = `${__dirname}/output/title-page-background-color.pdf`
     opts.to_file = outputFile
     opts.attributes = { stylesheet: `${__dirname}/../css/asciidoctor.css;${__dirname}/../css/document.css;${__dirname}/../css/features/book.css;${__dirname}/fixtures/black-title-page.css` }
-    await converter.convert(asciidoctor, `${__dirname}/fixtures/title-page.adoc`, opts, false)
+    await converter.convert(asciidoctor, { path: `${__dirname}/fixtures/title-page.adoc` }, opts, false)
     expect(outputFile).to.be.visuallyIdentical('title-page-background-color.pdf')
   })
 })