diff --git a/lib/cli.js b/lib/cli.js
index 46bfc71..821ba57 100755
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -3,10 +3,12 @@
const yargs = require('yargs')
const fs = require('fs')
-const path = require('path')
const asciidoctor = require('@asciidoctor/core')()
const pkg = require('../package.json')
const stdin = require('./stdin')
+const ospath = require('path')
+
+const DOT_RELATIVE_RX = new RegExp(`^\\.{1,2}[/${ospath.sep.replace('/', '').replace('\\', '\\\\')}]`)
function convertOptions (args) {
const backend = args['backend']
@@ -47,7 +49,14 @@ function convertOptions (args) {
console.log('destination-dir ' + destinationDir)
}
if (requireLib) {
- require(requireLib)
+ const lib = require(requireLib)
+ if (lib && typeof lib.register === 'function') {
+ // REMIND: it could be an extension or a converter.
+ // the register function on a converter does not take any argument
+ // but the register function on an extension expects one argument (the extension registry)
+ // Until we revisit the API for extension and converter, we pass the registry as the first argument
+ lib.register(asciidoctor.Extensions)
+ }
}
const verboseMode = quiet ? 0 : verbose ? 2 : 1
const attributes = []
@@ -271,6 +280,35 @@ function processFiles (files, verbose, timings, options) {
process.exit(code)
}
+function requireLibrary (requirePath, cwd = process.cwd()) {
+ if (requirePath.charAt(0) === '.' && DOT_RELATIVE_RX.test(requirePath)) {
+ // NOTE require resolves a dot-relative path relative to current file; resolve relative to cwd instead
+ requirePath = ospath.resolve(requirePath)
+ } else if (!ospath.isAbsolute(requirePath)) {
+ // NOTE appending node_modules prevents require from looking elsewhere before looking in these paths
+ const paths = [cwd, ospath.dirname(__dirname)].map((start) => ospath.join(start, 'node_modules'))
+ requirePath = require.resolve(requirePath, { paths })
+ }
+ return require(requirePath)
+}
+
+function prepareProcessor (argv, asciidoctor) {
+ const requirePaths = argv['require']
+ if (requirePaths) {
+ requirePaths.forEach(function (requirePath) {
+ const lib = requireLibrary(requirePath)
+ if (lib && typeof lib.register === 'function') {
+ // REMIND: it could be an extension or a converter.
+ // the register function on a converter does not take any argument
+ // but the register function on an extension expects one argument (the extension registry)
+ // Until we revisit the API for extension and converter, we pass the registry as the first argument
+ lib.register(asciidoctor.Extensions)
+ }
+ })
+ }
+}
+
+
function run (argv) {
const processArgs = argv.slice(2)
const args = argsParser().parse(processArgs)
@@ -282,6 +320,7 @@ function run (argv) {
args['out-file'] = args['out-file'] || '-'
}
const options = convertOptions(args)
+ prepareProcessor(args, asciidoctor)
if (stdin) {
convertFromStdin(options, args)
} else if (version || (verbose && files && files.length === 0)) {
@@ -293,7 +332,7 @@ function run (argv) {
processFiles(files, verbose, args['timings'], options)
} else {
if (args['help'] === 'syntax') {
- console.log(fs.readFileSync(path.join(__dirname, '..', 'data', 'reference', 'syntax.adoc'), 'utf8'))
+ console.log(fs.readFileSync(ospath.join(__dirname, '..', 'data', 'reference', 'syntax.adoc'), 'utf8'))
} else {
yargs.showHelp()
}
@@ -305,5 +344,6 @@ module.exports = {
argsParser: argsParser,
convertOptions: convertOptions,
processFiles: processFiles,
- processor: asciidoctor
+ processor: asciidoctor,
+ prepareProcessor: prepareProcessor
}
diff --git a/test/fixtures/blog-converter.js b/test/fixtures/blog-converter.js
new file mode 100644
index 0000000..cc224d6
--- /dev/null
+++ b/test/fixtures/blog-converter.js
@@ -0,0 +1,9 @@
+class BlogConverter {
+ convert () {
+ return '';
+ }
+}
+
+module.exports.register = function register () {
+ Opal.Asciidoctor.ConverterFactory.register(new BlogConverter(), ['blog']);
+}
diff --git a/test/fixtures/shout-ext.js b/test/fixtures/shout-ext.js
new file mode 100644
index 0000000..207a74c
--- /dev/null
+++ b/test/fixtures/shout-ext.js
@@ -0,0 +1,20 @@
+const shoutBlock = function () {
+ const self = this
+ self.named('shout')
+ self.onContext('paragraph')
+ self.process(function (parent, reader) {
+ const lines = reader.getLines().map(function (l) { return l.toUpperCase() })
+ return self.createBlock(parent, 'paragraph', lines)
+ })
+}
+
+module.exports.register = function register (registry) {
+ if (typeof registry.register === 'function') {
+ registry.register(function () {
+ this.block(shoutBlock)
+ })
+ } else if (typeof registry.block === 'function') {
+ registry.block(shoutBlock)
+ }
+ return registry
+}
diff --git a/test/test.js b/test/test.js
index 05d6f09..c3e74f9 100644
--- a/test/test.js
+++ b/test/test.js
@@ -7,7 +7,7 @@ const dirtyChai = require('dirty-chai')
chai.use(dirtyChai)
const stdin = require('../lib/stdin')
-const { run, processor, convertOptions, processFiles } = require('../lib/cli.js')
+const { run, processor, convertOptions, processFiles, prepareProcessor } = require('../lib/cli.js')
const argsParser = require('../lib/cli.js').argsParser()
describe('Arguments parser', () => {
@@ -182,3 +182,30 @@ describe('Process files', () => {
}
})
})
+
+describe('Require option', () => {
+ it('should require an extension library', () => {
+ const asciidoctor = require('@asciidoctor/core')()
+ try {
+ expect(Object.keys(asciidoctor.Extensions.getGroups()).length).to.equal(0)
+ prepareProcessor(argsParser.parse('one.adoc -r ./test/fixtures/shout-ext.js'), asciidoctor)
+ expect(Object.keys(asciidoctor.Extensions.getGroups()).length).to.equal(1)
+ } finally {
+ asciidoctor.Extensions.unregisterAll()
+ }
+ })
+
+ it('should require a converter library', () => {
+ const asciidoctor = require('@asciidoctor/core')()
+ try {
+ asciidoctor.convert('Hello', { backend: 'blog' })
+ expect.fail('blog backend should be missing')
+ } catch (e) {
+ expect(e.message).to.equal('asciidoctor: FAILED: missing converter for backend \'blog\'. Processing aborted.')
+ }
+ prepareProcessor(argsParser.parse('one.adoc -r ./test/fixtures/blog-converter.js'), asciidoctor)
+ const html = asciidoctor.convert('Hello', { backend: 'blog' })
+ expect(html).to.equal('')
+ })
+})
+