Hercule โ Transclusion Tool
Write large markdown documents, including API Blueprint, while keeping things DRY (don't repeat yourself).
Hercule is a command-line tool and library for transcluding markdown, API Blueprint, and plaintext. This allows complex and repetitive documents to be written as smaller logical documents, for improved consistency, reuse, and separation of concerns.
- Simple extension of markdown link syntax
:[Title](link.md)
(preceding colon:
) - Transclude local files
- Transclude remote (HTTP) files
- Transclude strings
- Smart indentation
- Stream, Async, Sync APIs
Note: synchronous API is only available in node 0.12 and above.
Installation
Install Hercule using npm:
npm install -g hercule
Usage
You can use Hercule as a command-line utility:
hercule src/blueprint.md -o output.md
Hercule supports processing input from stdin, and writing to stdout:
cat src/blueprint.md | hercule | less
Or you can use Hercule as a library:
var hercule = require('hercule');
var output = hercule.transcludeStringSync("# Title\n\n:[abstract](abstract.md)");
console.log(output);
Transclusion Syntax
The following examples use ES2015 syntax.
Basic transclusion (local files and remote HTTP files)
Hercule extends the Markdown inline link syntax with a leading colon (:
) to denote the link should transcluded.
import { transcludeStringSync } from 'hercule';
const input = 'This is an :[example link](foo.md).';
const output = transcludeStringSync(input);
console.log(output);
// This is an example transclusion.
Extending the standard Markdown link syntax means most markdown parsers will treat Hercule's transclusion links as standard Markdown links. For example, Github handles transclusion links in this manner.
Hercule is also able to transclude HTTP links.
import { transcludeStringSync } from 'hercule';
const input = 'Jackdaws love my :[size](https://raw.githubusercontent.com/jamesramsay/hercule/master/test/fixtures/basic/size.md) sphinx of quartz.';
const output = hercule.transcludeStringSync(input);
console.log(output);
// Jackdaws love my big sphinx of quartz.
Placeholders and overriding references
Placeholders (e.g. :[foo](bar)
) allow you create a target for transclusion without specifying the link in the document.
A parent document can then override the placeholder with the desired link.
Placeholders and references can be helpful for increasing the 'dryness' of your source documents, or allowing environmental variables to be passed into the document during processing.
import { transcludeStringSync } from 'hercule';
const input = ':[foo](bar)';
const options = {
references: [{
placeholder: 'bar',
href: 'fizz buzz',
hrefType: 'string'
}]
};
const output = transcludeStringSync(input, options);
console.log(output);
// fizz buzz
References are passed down to any nested transclusion links.
Default placeholders
Sometimes a file might be used in multiple contexts, some contexts requiring references and others not. Default placeholders help handle this situation more conveniently.
The following example uses Apiary's Markdown Syntax for Object Notation (MSON).
## Ingredient (object)
- id: 1 (number, required)
- name: Cucumber (string, required)
- description: Essential for tzatziki (string, :[is required](required || "optional"))
import { transcludeStringSync } from 'hercule';
const inputRequired = ':[Required Ingredient](cucmber.mson required:"required")';
const inputDefault = ':[Optional Ingredient](cucmber.mson)';
const outputRequired = transcludeStringSync(inputRequired);
console.log(outputRequired);
// ## Recipe (object)
//
// - id: 1 (number, required)
// - name: Cucumber (string, required)
// - description: Essential for tzatziki (string, required)
const outputDefault = transcludeStringSync(inputDefault);
console.log(outputDefault);
// ## Recipe (object)
//
// - id: 1 (number, required)
// - name: Cucumber (string, required)
// - description: Essential for tzatziki (string, optional)
Whitespace sensitivity
Leading whitespace is significant in Markdown. Hercule preserves whitespace at the beginning of each line.
Binary sort example:
:[](snippet.c)
Each line of snippet.c
will be indented with the whitespace preceding it.
Documentation
Some functions are available in both sync and async varieties.
TranscludeStream
transcludeString
,transcludeStringSync
transcludeFile
,transcludeFileSync
Note synchronous interfaces rely on execFileSync
which is only available with node 0.12 and above.
Async interfaces should work with node 0.10 and above.
TranscludeStream([options], [pathList])
Returns a duplex stream.
Arguments
options
- An object of options to be applied when processing input.relativePath
- A path to which the transclusion links within input stream are relative.references
- A collection of references which be considered when resolving each transclusion link. Each reference must containplaceholder
,href
, andhrefType
.placeholder
- A string used to locate the target(s) for transclusion.href
- A link which will be trancluded according to itshrefType
.hrefType
-file
,http
, orstring
parents
- A collection of fully qualified file paths of the input used to detect and prevent circular transclusion.
pathList
- An array (typically empty) which the path of every transclusion will be appended to. This is helpful for generating a watch list for live reloading.
Examples
import { TranscludeStream } from 'hercule';
const trancluder = new TranscludeStream();
transcluder.on('error', (err) => {
// Handle exceptions like dead links
console.log(err);
});
// assuming input is a readable stream and output is a writable stream
input.pipe(transcluder).pipe(output);
transcludeString(str, [options], callback)
Transcludes the input str
, and callback
is called when finished.
Arguments
str
- A string to process.options
- An object of options to be applied when processing input.relativePath
- A path to which the transclusion links within inputstr
are relative.
callback(err, [output], [sourcePaths])
- A callback which is called after the inputstr
has been processed.callback
will be passed an error, processed output and array of source document file paths.
Omit the callback
if using transcludeStringSync
. Only output
will be returned.
Examples
// async
import { trancludeString } from 'hercule';
trancludeString(':[foo](bar.md)', (err, output) => {
// Handle exceptions like dead links
if (err) console.log(err)
console.log(output);
});
// sync
import { trancludeStringSync } from 'hercule';
try {
var output = trancludeFileSync('bar.md');
console.log(output);
} catch (ex) {
// Handle exceptions like dead links
console.log(ex);
}
transcludeFile(filepath, [options], callback)
Transcludes the file at the provided filepath
, and callback
is called when finished.
Arguments
filepath
- A path to a file to process.options
- An object of options to be applied when processing input.relativePath
- A path to which the inputfilepath
is relative.
callback(err, [output], [sourcePaths])
- A callback which is called after the file at the providedfilepath
has been processed.callback
will be passed an error, processed output and array of source document file paths.
Omit the callback
if using transcludeFileSync
. Only output
will be returned.
Examples
// async
import { trancludeFile } from 'hercule';
trancludeFileSync('foo.md', (err, output) => {
// Handle exceptions like dead links
if (err) console.log(err)
console.log(output);
});
// sync
import { trancludeFileSync } from 'hercule';
try {
var output = trancludeFileSync('foo.md');
console.log(output);
} catch (ex) {
// Handle exceptions like dead links
console.log(ex);
}