npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@eit6609/markdown-templates

v1.0.0

Published

Natural templates for markdown

Downloads

4

Readme

Markdown Templates

Markdown Templates is a natural template engine for markdown.

The concept of "natural template" comes from Thymeleaf, a Java template engine for HTML.

Quoting the Thymeleaf homepage:

Thymeleaf's main goal is to bring elegant natural templates to your development workflow — HTML that can be correctly displayed in browsers and also work as static prototypes, allowing for stronger collaboration in development teams.

Run this to install:

npm i @eit6609/markdown-templates

Natural Templates for Markdown

In practice, the templates of Markdown Templates are valid markdown files, that can be rendered correctly by the preview of your favourite markdown editor. They even have the .md extension.

But Markdown Templates brings the concept of natural templating a bit further: the preview can be used to actually guide you while you are writing the template: if it looks good, you are doing good.

This is the main (or the only) reason for the existence of this template engine, because of course for markdown you can use any of the template engines out there that are not dedicated to a specific language, such as the Mustache family, EJS, you name it.

What enables the whole thing is the fact that markdown has syntactic constructs dedicated to the display of code. You know them:

  • inline code, like `this`
  • code blocks, created using tabs or spaces
  • fenced code blocks, opened and closed by ```

Now, what about executing this code?

We could:

  • replace the inline code with its evaluation
  • use the code blocks to control the result with conditionals and loops

Markdown Templates does just that.

A Quick Example

Given this template:

Hello `name`!

Your basket contains the following items:

    for (let i = 0; i < items.length; i++) {

* `items[i]`

`-`

    }
    if (freeShipping) {

Your order is eligible for free shipping.

    }

and this context:

{
    name: 'Bob',
    items: ['scissors', 'paper', 'rock'],
    freeShipping: true
}

the result is:

Hello Bob!

Your basket contains the following items:

* scissors
* paper
* rock

Your order is eligible for free shipping.

The preview of the template could look like this:


Hello name!

Your order contains the following items:

for (let i = 0; i < items.length; i++) {
  • items[i]

-

}
if (freeShipping) {

Your order is eligible for free shipping.

}

The preview displays neatly and nicely the difference between the static and the dynamic parts, in a natural way.

Moreover, there are no ugly {{ and }} or even uglier <% and %>.

By now you'll have at least a couple of questions:

  • what if I want an inline code or code block to actually display the code, and not execute it?
  • what is that - that you put before the closing bracket of the for loop?

The answers are in the following section.

Template Reference

Code execution

This is the way the code is executed:

  • expressions inside an inline code, like `blue + red - 1`, are substituted by their evaluation.
  • the code blocks control the appearing in the output of the text they enclose. Since to close a code block a blank line is needed, that line is considered part of the code block and will not go to the output.

Escaping code

Instead of leaving to the programmer the burden to mark the code to execute, this engine reverse the perspective and asks the programmer to mark the code that he or she does not want to be executed.

The rules fore this escaping are:

  • inline code is not substituted if it starts with !.
  • a block code is not executed if the first non-blank character of the line is !.
  • the code inside fenced code blocks is never executed and it does not need to be escaped. However, inside the code fence, the above rules still apply.

Example

This is the template:

Don't substitute here: `!name`. But substitute here, `name`.

    if (bonus) {

This is a bonus paragraph.

    }

```
This is static code, but you can still substitute things, `name`.
```

    !this.is.static.code()

And this is the end.

This is the data:

{ name: 'Bob', bonus: true }

And this is the output:

Don't substitute here: `name`. But substitute here, Bob.

This is a bonus paragraph.

```
This is static code, but you can still substitute things, Bob.
```

    this.is.static.code()

And this is the end.

Separator

There's only one drawback using code blocks like this: you need to add a non empty paragraph to separate the text from the code block, but most of the times this extra paragraph is not something that you want in the output.

Therefore the engine understands a special paragraph that contains only the characters backtick-hyphen-backtick. This paragraph acts as a no-op and it's treated like this:

  • the empty line preceding the no-op, the no-op itself and the empty line following it are ignored.

We could choose just any uncommon string, but an inline code is more suitable because it is highlighted in the preview.

Example

    for (let i = 0; i < items.length; i++) {

* `items[i]`

`-`

    }

The no-op is needed because the only way to separate the list item from the closing bracket is with a non empty paragraph. This special non empty paragraph will do the job without appearing in the output, which is something like:

* scissors
* paper
* rock

Again, the preview will tell you that there is something wrong if you are not separating the code block from the text correctly. This template:

    for (let i = 0; i < items.length; i++) {

* `items[i]`
    }

will be rendered like this:


for (let i = 0; i < items.length; i++) {
  • items[i] }

Add the no-op and you'll see the preview showing something that looks right:


for (let i = 0; i < items.length; i++) {
  • items[i]

-

}

API Reference

compile(template: string, options?: object): function(locals: string): string

This function gets a template and it compiles it into a function that, invoked with the data as its parameter, will generate the output.

The function code is enclosed in a with block, to let you access the properties of the parameter directly.

Parameters:

  • The template parameter contains the template.
  • The options parameter is an object that can have the following properties:
    • with, boolean, optional, default true. With false you can disable the generation of the with block if you incur in one of the problems it may give.
    • locals, string, optional, default locals. If you disable the with block generation, you need to access explicitly the parameter of the function. The default name of the parameter is locals, but you can change it with this option.
    • debug, boolean, optional, default false. If true, it triggers the display (with console.log()) of the generated function.

compileFile(fileName: string, options?: object): function(locals: string): string

Exactly like compile(), but it takes a file name as first parameter and it (synchronously) reads the template from the file.