Skip to content

Latest commit

 

History

History
337 lines (235 loc) · 10.3 KB

README.md

File metadata and controls

337 lines (235 loc) · 10.3 KB

Hercule – Transclusion Tool

Version License Build Status Coverage Status Dependency Status styled with prettier

Hercule

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

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:

import { trancludeString } from 'hercule';

trancludeString('# Title\n\n:[abstract](abstract.md)', (err, output) => {
  if (err) console.log(err)
  console.log(output);
});

Transclusion 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 { 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 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 { 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.

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 { 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)
});

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


Returns a duplex stream.

Arguments

  1. source (String): A string used for resolving relative links and generating sourcemap.
  2. 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

  1. str (String): A string to process.
  2. 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'.
  1. callback(err, [output], [sourcemap]) (Function): A function that will be called after the input str 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

  1. source (String): A path to a file to process.
  2. 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'.
  1. callback(err, [output], [sourcemap]) (Function): A function that will be called after the source 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

  1. url - A relative url from the input being processed.
  2. 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: