0.10.0 • Published 1 year ago

emdd v0.10.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

Extensible markdown

Extensible Markdown Documents (emdd) is an extension to Markdown for writing web documents, which can then be transpiled with the use of plugins to your desired output, or lack of. This allows Markdown to remain relatively as normal while also promoting extensibility. By default, md files will have their Markdown content parsed and output as HTML. Blocks annotated with an @ specifier will be processed via pre-processing and content processing plugins where supported.

The API for Extensible Markdown will be classed as unstable until a version 1 release is reached

The reasoning behind Extensible Markdown

Markdown serves the developer community quite kindly, offering a simple and fast way of writing prose. The major problem to this is when you want something a bit more in your Markdown, this is the reason for creating this project.

If you like this project, feel free to use it as you please.

If you would like to contribute to Extensible Markdown, please visit the GitHub repository for this page and submit a pull request with your contribution (contribution guidelines do not currently exist).

Example document

The following is an example of an EXMD document:

@docArgs()
```
"title": "Example page"
```

# Title

Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. 
Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. 
Some paragraph text. Some paragraph text. Some paragraph text.

@js()
```
let sum = 3 + 3;
return `<p>Sum of 3 + 3 is ${sum}</p>`;
```

After transpilation to HTML, the contents of the document would roughly look as follows:

<html>
    <head>
        <title>Example page</title>
    </head>
    <body>

        <h1>Title</h1>
        <p>
            Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text.
            Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text. Some paragraph text.
            Some paragraph text. Some paragraph text. Some paragraph text.
        </p>

        <p>Sum of 3 + 3 is 6</p>
    </body>
</html>

Usage guide

First, install EMDD using npm:

npm install emdd

Currently, EMDD can be used by programatically building the transpiler in your program. A static site generator is also included which can be executed from the command-line by passing the location of a valid site configuration file.

Programmatic usage

First, we must create a valid emdd document:

const emdd = `# My title

Some text

@js()
```
let sum = 3 + 3;
return `<p>Sum of 3 + 3 is ${sum}</p>`;
```

Some more markdown following the @ block.

Inline plugins @js(value="return 3 + 3"); should be supported.
`;

Now, we can create a new Tokeniser. We must pass the md source content and an array of plugin identifiers. The plugin identifiers are the names of the content transformation plugins you are using in your document, a content transformation plugin is used to transform an @ block of content.

const tokeniser = new Tokeniser(emdd, ["js", "lit", "docArgs"]);
const tokens = tokeniser.tokenise();

After calling the tokenisers tokenise() method, we can then parse the tokens into blocks. A block represents a part of the document, whether Markdown, a plugin or an inline-plugin. To do this, create a new parser and call its parse() method:

const parser = new Parser(tokens);
const blocks = parser.parse();

Now that we have our blocks, we can use a Transpiler to transform our emdd source into the desired output. First things first though, let's create our plugins:

const contentTransformerPlugins = [new JSTransformer(), new LiteralTransformer(), new DocumentArgumentsTransformer()];
const htmlDocumentTransformer = new HtmlDocumentTransformer();

The contentTransformerPlugins are used to execute the @ blocks and return a desired output, the htmlDocumentTransformer is used after all of the Markdown has been converted to HTML (and content transformers applied) to convert the transpiled content to a HTML document.

Create a new instance of Transpiler and pass it the content transformation plugins as input, then call transpile() and pass it the blocks to convert and the document transformer desired:

const transpiler = new Transpiler(contentTransformerPlugins);
console.log(transpiler.transpile(blocks, htmlDocumentTransformer));

EMDD Syntax and other rules

This section concerns the syntactical and other rules of emdd.

@ Blocks

The general syntax for an @ block is as follows:

@<PLUGIN_IDENTIFIER>(ARG1="VALUE" ARG2="VALUE" ...)
```
<CONTENT>
```

First, an @ symbol preceeds the identifier of the plugin. This is immediately followed by a space delimited parameter list surrounded by parenthesis; there must be no space between the identifier and the opening parenthesis to be valid. Each parameter is a KEY="VALUE" pair where the KEY is a sequence of alpha characters and the value is within double quotes.

After the parameters closing parenthesis must immediately follow three backticks on a line to themself, with the content on the following lines. To signify the end of the content, three backticks on a line by themselves must be used. To allow nesting of this pattern (```).

Block level

The @<PLUGIN_IDENTIFIER>(ARG1 ARG2 ...) syntax allows the use of a plugin to do something with the contents of its block. An example could be executing some JavaScript and placing the result in the output document:

@js()
```
let sum = 3 + 3;
return `<p>Sum of 3 + 3 is ${sum}</p>`;
```

Plugins may also be passed arguments that they expect, we can use the defer and name parameters to defer the execution of a JS block. Instead, a function with the given name is added to the global context object of the plugin (NOT IMPLEMENTED YET):

@js(defer="true" name="doCalculation")
```
let sum = 3 + 3;
return `<p>Sum of 3 + 3 is ${sum}</p>`;
```

We can then use the function at a later stage by accessing the context object:

Putting some math here @js(value="return context.doCalculation();");

In-line block level

If an @ block is used in-line with Markdown content, it's result will be combined with the markdown. For example:

Today is a lovely day, at the time of writing it is day @js(value="return new Date().day() + 1;"); of the week.

could become:

<p>Today is a lovely day, at the time of writing it is day 4 of the week.</p>

Built-in plugins

JS Plugin

The JavaScript plugin is almost like having a <script> element in your HTML, it allows you to execute JavaScript but during build-time instead of your websites runtime. Each block you create represents a function:

@js()
```
console.log("Hello");
return "some text";
```

The return of a function is output into the document. Return nothing to place nothing into the document.

Context

The JS plugin provides a context, this context persists between blocks and documents as long as the same instance of JSPlugin is used:

@js()
```
context.i = 50;
let sum = 3 + 3;
return `<p>Sum of 3 + 3 is ${sum}</p>`;
```

@js()
```
console.log(context.i);
return `<p>i was ${context.i}</p>`;
```

To access the shared context, a global state, use the context variable which is automatically accessible.

Static site generation

To generate a static site, create a valid configuration and pass this to the generateFromConfig() method of an EMDDSiteGenerator:

const config = loadSiteConfiguration(configLocation);
const generator = new EMDDSiteGenerator();
generator.generateFromConfig(config);

A valid configuration would look like:

{
    "output": {
        "type": "html5",
        "directory": "./build"
    },
    "src": {
        "entrypoint": "./index.emdd",
        "copyFilesOfType": ["html", "css", "js"]
    },
    "contentPlugins": ["js", "lit", "docArgs"]
}

Supported output.types include:

  • html5

The output.directory is where the build is output.

The src.entrypoint is the file which becomes the index.html file, if left blank it is expected that you supply an index.html file (NOT IMPLEMENTED).

The src.copyFilesOfType list allows you to specify file types to copy over directly as is. Files which are not .emdd or specified in src.copyFilesOfType are ignored.

Generating a site from the command-line

To generate a site from the command-line, run this npm package and pass the --config flag:

npx emdd site --config="./config.json"

Creating plugins

Plugins are passed to the transpiler to execute some custom functionality.

Content transformation plugins

A content transformation plugin is created by extending the ContentTransformerPlugin class, this has one method and a constructor which accepts a name and parameters for the plugin.

class ExamplePlugin extends ContentTransformerPlugin {
    constructor() {
        super("example", ["arg1"]);
    }

    transform(block) {
        // Do something with the block
    }
}

The block passed to visit() is a block containing the parameter arguments and its content. You may use many ContentTransformerPlugins in a single instance of EMDD.

You can also get the name and parameters of a plugin using their associated getters:

const name = plugin.name;
const params = plugin.parameters;

Pre-processing hook

We can also create ContentTransformerPlugins which are hooked to run before all other content plugins, the template plugin is a useful example of a pre-processing plugin:

@template(name="title" args="title lead")
```
<header>
    <h1>@title;</h1>
    <p>@lead;</p>
</header>
```
  • Use @ followed by an argument name and then a ; to reference an argument in a template

This plugin will register a template with a WeaveTemplatePlugin instance which can then be weaved during the final stage of processing into the desired content:

@weave(name="title")
```
"title": "Hello world", 
"lead": "Some lead text"
```

The arguments for a template are weaved from a JSON object or a JS object containing properties with the same names. A plugin must have pre-processing enabled to be used in this way.

Document transformation plugins

An instance of a DocumentTransformerPlugin will take the output of parsing the Markdown and applying the plugins, producing the desired output. This could be a JSX component or a HTML document as quick examples.

A DocumentTransformerPlugin must implement a single method, transform(content, args). The args are supplied if a @docType() block is specified in the .md source being converted.

class CustomDocTransformPlugin extends DocumentTransformationPlugin {
    transform(content, args) {
        // do stuff
    }
}
0.10.0

1 year ago

0.9.0

1 year ago

0.8.0

1 year ago

0.7.4

1 year ago

0.7.3

1 year ago

0.7.2

1 year ago

0.7.1

1 year ago

0.7.0

1 year ago

0.6.0

1 year ago

0.5.0

1 year ago

0.4.1

1 year ago

0.4.0

1 year ago

0.3.2

1 year ago

0.3.1

1 year ago

0.3.0

1 year ago