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
Install Hercule using npm:
$ npm install -g hercule
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:
import { trancludeString } from 'hercule';
trancludeString('# Title\n\n:[abstract](abstract.md)', (err, output) => {
if (err) console.log(err)
console.log(output);
});
Hercule extends the Markdown inline link syntax with a leading colon (:
) to denote the link should transcluded.
import { trancludeString } from 'hercule';
trancludeString('This is an :[example link](foo.md).', (err, output) => {
if (err) console.log(err)
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 { trancludeString } from 'hercule';
input = 'Jackdaws love my :[size](https://raw.githubusercontent.com/jamesramsay/hercule/master/test/fixtures/basic/size.md) sphinx of quartz.';
trancludeString(input, (err, output) => {
if (err) console.log(err)
console.log(output);
// Jackdaws love my big sphinx of quartz.
});
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 { transcludeString } from 'hercule';
const input = ':[foo](bar)';
const options = {
references: [{
placeholder: 'bar',
href: 'fizz buzz',
hrefType: 'string'
}]
};
trancludeString(input, (err, output) => {
if (err) console.log(err)
console.log(output);
// fizz buzz
});
References are passed down to any nested transclusion links.
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 { transcludeString } from 'hercule';
const inputRequired = ':[Required Ingredient](cucmber.mson required:"required")';
const inputDefault = ':[Optional Ingredient](cucmber.mson)';
trancludeString(inputRequired, (err, output) => {
if (err) console.log(err)
console.log(output);
// ## Recipe (object)
//
// - id: 1 (number, required)
// - name: Cucumber (string, required)
// - description: Essential for tzatziki (string, required)
});
trancludeString(inputDefault, (err, output) => {
if (err) console.log(err)
console.log(output);
// ## Recipe (object)
//
// - id: 1 (number, required)
// - name: Cucumber (string, required)
// - description: Essential for tzatziki (string, optional)
});
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.
Returns a duplex stream.
Arguments
source
(String): A string used for resolving relative links and generating sourcemap.options
(Object): An object of options to be applied when processing input.
resolvers
(Array[Function]): An array of functions which are applied to resolve the URLs to content.transclusionSyntax
(String): Choose transclusion link syntax. Supports 'hercule', 'aglio', 'marked', 'multimarkdown'.
Customer Emitters
sourcemap
(Object): A sourcemap object will be emitted exactly once.
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);
Transcludes the input str
, and callback
is called when finished.
Arguments
str
(String): A string to process.options
(Object): An object of options to be applied when processing input.
source
(String): source file required for resolving relative links and generating sourcemap.resolvers
(Array[Function]): An array of functions which are applied to resolve the URLs to content.transclusionSyntax
(String): Choose transclusion link syntax. Supports 'hercule', 'aglio', 'marked', 'multimarkdown'.
callback(err, [output], [sourcemap])
(Function): A function that will be called after the inputstr
has been processed.
err
(Error): An error object.output
(String): A string containing processed input.sourcemap
(Object): A sourcemap object.
Examples
import { trancludeString } from 'hercule';
trancludeString(':[foo](bar.md)', (err, output) => {
// Handle exceptions like dead links
if (err) console.log(err)
console.log(output);
});
Transcludes the source
file.
Arguments
source
(String): A path to a file to process.options
(Object): An object of options to be applied when processing input.
resolvers
(Array[Function]): An array of functions which are applied to resolve the URLs to content.transclusionSyntax
(String): Choose transclusion link syntax. Supports 'hercule', 'aglio', 'marked', 'multimarkdown'.
callback(err, [output], [sourcemap])
(Function): A function that will be called after thesource
file has been processed.
err
(Error): An error object.output
(String): A string containing processed input.sourcemap
(Object): A sourcemap object.
Examples
import { trancludeFile } from 'hercule';
trancludeFile('foo.md', (err, output) => {
// Handle exceptions like dead links
if (err) console.log(err)
console.log(output);
});
Resolver functions transform urls into the input to be transcluded.
Hercule includes resolvers for http urls, local files, and strings. You can replace these with your own resolvers to customise the behaviour.
Arguments
url
- A relative url from the input being processed.source
- The absolute source url of the url being resolved.
Returns
- (null): Returns null if the url cannot be resolved.
- (Object)
content
(Stream | String): The content to be transcluded. Streams are processed for further transclusion links. Strings are assumed fully processed.url
(String): The absolute url of the input, allowing circular reference detection and nested transclusion.
Examples
import { trancludeFile, resolveHttpUrl, resolveLocalUrl, resolveString } from 'hercule';
function myResolver(url, source) {
// Add your implementation here
// Return null to try next resolver
return null;
}
// Resolvers are tried in order
const resolvers = [myResolver, resolveHttpUrl, resolveLocalUrl, resolveString];
trancludeFile('foo.md', { resolvers }, (err, output) => {
// Handle exceptions like dead links
if (err) console.log(err)
console.log(output);
});
Hercule also has basic support for alternative transclusion link syntax, including:
- aglio:
<!-- include(foo.md) -->
- Marked:
<<[sections/section1.md]
- MultiMarkdown:
{{bar.md}}