HTTPS SSH

uMacro

uMacro is a simple tool that is capable of transforming a string of text containing macros (a.k.a. shortcodes) into desired output, enabling easy insertion of valid HTML embedded content. Several default macros are provided, however, any macro output can be produced with the addition of custom macros..

Example input:

[youtube id="ykwqXuMPsoc"]

The above macro will be transformed to the following HTML code:

<iframe width="560" height="315" src="http://www.youtube.com/embed/ykwqXuMPsoc" frameborder="0"></iframe>

Build and test

Production build (as delivered via npm):

npm run build

Dev build:

npm run dev

Run unit tests:

npm test

Run mutation tests:

npm run stryker

Development

During development, changes to files will be automatically monitored. TypeScript will be compiled to JS and unit tests will be launched automatically.

npm start

Documentation

What's a macro?

A macro (also known as "shortcode") is an easy to write, mostly human-readable string of text that uMacro will substitute with output that is normally much more difficult to write.

A macro, in order to be recognised, must have a predefined structure. While mostly configurable, every macro will consist at least of the following parts, in this exact order:

  • opening delimiter
  • macro name
  • closing delimiter

Should additional parametres be used, the macro will include two additional parts, also in this exact order:

  • opening delimiter
  • macro name
  • separator
  • parametres
  • closing delimiter

Consider the following example macro:

[youtube id="dQw4w9WgXcQ"]
  • The opening delimiter is [.
  • The macro name is youtube.
  • The separator is (a space).
  • The parametre list is id="dQw4w9WgXcQ".
  • The closing delimiter is ].

The above rules apply to self-closing macros. A macro can also be enclosing, in which case additional parts come into play:

  • opening delimiter
  • macro name
  • separator
  • parametres
  • closing delimiter
  • body
  • macro close tag

The macro close tag always has the following structure:

  • opening delimiter
  • / (slash character)
  • macro name
  • closing delimiter

An example of a correctly formatted enclosing macro:

[button]Click me[/button]

Enclosing macros can contain other macros in their body, but since they are translated 1:1 to HTML, they must conform to the same rules as HTML tags do. Specifically, closing macros must be done at the same nesting level as opening them, e.g.:

[figure]
    [youtube id="dQw4w9WgXcQ"]
    [caption]A very good video![/button]
[/figure]

Valid macro names

A macro name is valid when it matches the regular expression /\w+/. In other words, a macro name may contain any number of uppercase and lowercase letters, numbers and underscores. A macro may not contain any whitespace or any non-alphanumeric characters other than the underscore.

Examples of valid macro names:

  • youtube
  • YouTube
  • you_tube
  • youtube2

Examples of invalid macro names:

  • $invalid
  • i am invalid
  • not-valid

Basic usage

The most basic usage of uMacro is just instantiating the Parser object and using its run() method, passing it an input: a string of text containing macros that uMacro can recognise.

TypeScript:

import {Parser} from "umacro";

const parser = new Parser();
const output = parser.run(input);

JavaScript:

const Parser = require("umacro").Parser;

const parser = new Parser();
const output = parser.run(input);

The run() method accepts a string input, parses the macros contained in it and returns a string with the macros replaced by their parsed output.

The Parser constructor accepts an optional config object that may include the following properties:

  • open (string): the opening delimiter of a macro; defaults to "[".
  • close (string): the matching closing delimiter; defaults to "]".
  • separator (string): separator that appears between the macro name and the parametre list; defaults to " " (a space).
  • paramParser (an instance of the ParamParser interface): parser used to read macro parametres; defaults to an instance of XMLAttributes.

TypeScript:

import {Parser, QueryString} from "umacro";

const parser = new Parser({
    open: "[[",
    close: "]]",
    separator: "?",
    paramParser: new QueryString() 
});

JavaScript:

const Parser = require("umacro").Parser;
const QueryString = require("umacro").QueryString;

const parser = new Parser({
    open: "[[",
    close: "]]",
    separator: "?",
    paramParser: new QueryString()
});

Available macros

youtube

Produces a YouTube clip in an iframe. Self-closing.

Parametres:

  • id: the YouTube clip identifier. Required.
  • width: the iframe width attribute value; defaults to 560
  • height: the iframe height attribute value; default to 315

Examples:

  • [youtube id="dQw4w9WgXcQ"]
  • [youtube id="dQw4w9WgXcQ" width="640" height="480"]

vimeo

Produces a Vimeo clip in an iframe. Self-closing.

Parametres:

  • id: the Vimeo clip identifier. Required.
  • width: the iframe width attribute value; defaults to 560
  • height: the iframe height attribute value; default to 315

Examples:

  • [vimeo id="71336599"]
  • [vimeo id="71336599" width="640" height="480"]

button

Produces a button element. Enclosing.

Parametres:

  • id: the content of the id HTML attribute. Optional.
  • class: the content of the class HTML attribute. Optional.

Examples:

  • [button]Click me[/button]
  • [button id="foo" class="bar"]Click me[/button]

figure

Produces a figure element. Enclosing.

Parametres:

  • id: the content of the id HTML attribute. Optional.
  • class: the content of the class HTML attribute. Optional.

Examples:

  • [figure]<img src="http://i.imgur.com/hZ3AlAn.jpg" />[/figure]
  • [figure id="foo" class="bar"]<img src="http://i.imgur.com/hZ3AlAn.jpg" />[/figure]

caption

Produces a figcaption element. Enclosing. Should be used inside the body of a figure macro or element. Takes no parametres.

Example:

  • [caption]Hello world[/caption]

Adding custom macros to the parser

It is possible to make the parser understand new macros. The method addMacro() serves this purpose:

TypeScript/JavaScript:

parser.addMacro("myMacro", new MyMacro());

As seen in the example, the addMacro() method accepts two arguments:

  • name (string): the name of the macro; this will correspond to the macro name part of a valid macro structure; it must be a valid macro name.
  • macro (object implementing the Macro interface): an instance of a macro builder. It must implement the Macro interface, ie. it must possess a run() method that accepts an object containing macro parametres and produces a string output.

Creating custom macros

In order to create an instance of a macro builder using the macro factory, it is enough to use the MacroFactory class and its static method makeInstance(). Alternatively, the method make() can be used to create a macro constructor which, when instantiated, will be the same as if it was created using makeInstance().

Both methods accept two arguments:

  • paramsConfig (object): an object containing the definition of parametres accepted by the macro (see below for more information)
  • outputFunction (function): a callback function that returns a string with the macro output. Takes one or two arguments. The first is always an object containing parametres, the optional second one is the macro body (used for enclosing macros).

The param configuration is an object whose keys are parametre names. A key's value must be an object with one or both of the following properties:

  • required (boolean): indicates whether the parametre is required
  • defaultValue (string): default value for parametres that are not passed in in the macro, but are otherwise needed to build the macro output.

If a parametre is required, it shouldn't have a default value (required = the user should provide it each and every time the macro is used). If a parametre has a default value, it is considered a non-required parametre (the user needn't specify it in order for the macro to work), however, the output function will always receive it. If the output function doesn't need to receive a given parametre at all, it is non-required and has no default value.

TypeScript:

import {MacroFactory} from "umacro";

const paramsConfig = {
    id: {required: true},
    a: {required: false, defaultValue: "foo"},
    b: {defaultValue: "bar"},
    c: {required: false},
    d: {}
};
const outFunc = (params: any, body?: string): string => {
    return `ID: ${params.id}`;
};
const myCustomMacro = MacroFactory.makeInstance(paramsConfig, outFunc);

or:

const MyCustomMacro = MacroFactory.make(paramsConfig, outFunc);
const myCustomMacro = new MyCustomMacro();

JavaScript:

const MacroFactory = require("umacro").MacroFactory;

const paramsConfig = {
    id: {required: true},
    a: {required: false, defaultValue: "foo"},
    b: {defaultValue: "bar"},
    c: {required: false},
    d: {}
};
const outFunc = (params, body) => {
    return "ID: " + params.id;
};
const myCustomMacro = MacroFactory.makeInstance(paramsConfig, outFunc);

or:

const MyCustomMacro = MacroFactory.make(paramsConfig, outFunc);
const myCustomMacro = new MyCustomMacro();

Creating custom parametre parsers

Creating macro parsers is done using the ParamParserFactory object and its make() or makeInstance() methods. The former creates a constructor for a parametre parser, while the latter creates an instance.

Both methods take one argument: a function accepting a string input with the parametres to be parsed and returning an object with the parsed parametre values. Examples:

TypeScript:

import {ParamParserFactory} from "umacro";

const parseFunction = (input: string): {} => {
    return JSON.parse(input);
};
const myParamParser = ParamParserFactory.makeInstance(parseFunction);

or:

const MyParamParser = ParamParserFactory.make(parseFunction);
const myParamParser = new MyParamParser();

JavaScript:

const ParamParserFactory = require("umacro").ParamParserFactory;

const parseFunction = function(input) {
    return JSON.parse(input);
};
const myParamParser = ParamParserFactory.makeInstance(parseFunction);

or:

const MyParamParser = ParamParserFactory.make(parseFunction);
const myParamParser = new MyParamParser();

Predefined param parsers

There are two predefined parametre parsers: QueryString and XMLAttributes. The former is the default one, so if it is to be used in a macro parser, it needn't be passed in the configuration object. If the latter is to be used, the configuration object should include it:

TypeScript:

import {Parser, XMLAttributes} from "umacro";

let parser = new Parser({
    paramParser: new XMLAttributes()
});

JavaScript:

const Parser = require("umacro").Parser;
const XMLAttributes = require("umacro").XMLAttributes;

const parser = new Parser({
    paramParser: new XMLAttributes()
});

Each of the param parsers may also be used as a standalone utility:

TypeScript:

import {QueryString} from "umacro";

const qs = new QueryString();
const parsed = qs.parse("foo=bar&bat=man");

JavaScript:

const QueryString = require("umacro").QueryString;

const qs = new QueryString();
const parsed = qs.parse("foo=bar&bat=man");