sub

subcode

A bootstrapped javascript template engine that features compile and runtime control code, custom syntax, compile-time includes, async templates and much more

Showing:

Popularity

Downloads/wk

1

Maintenance

No Maintenance Data Available

Package

Dependencies

3

Size (min+gzip)

3.0KB

License

MIT

Type Definitions

Tree-Shakeable

No?

Categories

Readme

subcode

Travis npm npm

A bootstrapped javascript template engine that features compile and runtime control code, custom syntax, compile-time includes, async templates and much more.

Overview

Upgrade Guide

If you have previously used a version below 3.0.0 look here for instructions on how to upgrade.


Installation

npm install subcode

Syntax

SyntaxFunction
<?= some code ?>Output html escaped
<?- some code ?>Output unescaped
<? some code ?>Runtime control code
<?: some code ?>Async code that is executed at compile time. await can be used.
<?# comment ?>A comment with no output.
<??Outputs <?

Runtime Output

There are two types of output:

TypeExample
Html-escaped output<?= 'a & b' ?> outputs a &amp; b
Unescaped output<?- 'a & b' ?> outputs a & b

Never put a semicolon at the end of an output tag. Something like <?= some.code; ?> will cause load errors!

Runtime Control Code

Runtime control code is just normal javascript code that is executed while rendering the template.
A few examples:

<? if (some.condition) { ?>
    <h1><?= output.some.heading ?></h1>
<? } ?>

<ul>
    <? for (const item of locals.items) { ?>
        <li><?= item.name ?></li>
    <? } ?>
</ul>

Compile-time Code

Compile-time code is used to extend the compiled render function. It is executed while building the render function so that it can embed custom code like included templates, embedded resources or other utility. In compile-time code, await can be used.


Compilation API

const {compile} = require('subcode');

// Compile a template directly:
const template1 = await compile(src, options);

// Or compile from file:
const template2 = await compile.file(filename, options);

// Render a template:
const html = template(locals);
  • src <string> - The template code.
  • filename <string> - The template filename.
  • options <object> - Optional compilation options:
    • syntax <object> - A custom syntax definition as described below.
    • filename <string> - The filename of the template used for the module system and relative includes. This option will be set automatically when using compile.file
    • encoding <string> - The encoding for reading template files. Default is 'utf8'
    • extend <function> - A function that is called with the compile time context for each template before running the bootstrapped compiler.
    • cache <Map> - The cache that is used for caching compiled templates.
    • async <boolean> - True to compile to an async render function so that await can be used from runtime template code.
  • returns <function> - The template render function with a single argument:
    • locals <object> - The locals object that is accessible from inside the template using locals.
    • returns <string> - A string of rendered html.

Compile-time Context

All properties from the compile-time context are available from compile-time code like global variables.

async context.include(name, request[, options])

Used for including template files.

<!-- Include the template: -->
<?: await include(name, request); ?>
<?: await include('myTemplate', 'path/to/template.html'); ?>

<!-- Use the template: -->
<?- myTemplate(locals) ?>
  • name <string> - The name of the embedded render function.
  • request <string> - The filename to include. Relative paths require the filename compile option in the current template.
  • options <object> - An object with compile options expect that the syntax, extend, encoding and cache options will default to the current template's options.

async context.includeAll(map)

Used for including multiple template files.

<?: await includeAll(map); ?>
<?: await includeAll({
    template1: 'path/to/template1.html',
    template2: ['path/to/template2.html', {some: 'compile-options'}]
}); ?>

<?- template1(locals) ?>
<?- template2(locals) ?>
  • map <object> - An object with templates to include.
    • key <string> - Map keys are used as template names.
    • value <string> | <array> - The template path or an array of the template path with compile options.

context.template(name, [options, ]body)

Compile a nested template into an embedded render function.

<?: template('myTemplate', () => { ?>
    <!-- The template body is just template code! -->
    <p><?= locals.text ?></p>
<?: }); ?>

<!-- Use the template: -->
<?- myTemplate({text: 'Hello World'}) ?>
  • name <string> - The name of the embedded render function.
  • options <object> - An object which may set the async compile option.
  • body <function> - The body of a template.

context.output(html)

Output runtime code to render raw html at runtime.

runtime, <?: output('compile-time'); ?>

Would render to

runtime, compile-time
  • html <string> - A string of raw html that is embedded into the template.

context.embedObject(name, data)

Embed data into the templates runtime.

<!-- Embed data into the template runtime: -->
<?: embedObject('example', {foo: 'bar'}) ?>

<!-- Use it at runtime: -->
Foo: <?= example.foo ?>
  • name <string> - The name of the embedded object.
  • data <any> - The object to embed. The object will be stringified using stringify-object.

context.write(code)

Write javascript code to the render function.

<!-- Embed some data: -->
<?: write('const magic = 42;'); ?>

<!-- Use at runtime: -->
<?= magic ?>
  • code <string> - Some javascript to write. If the passed code should be independent from other parts in the render function, make sure to append a semicolon at the end.

context.stringEscape(str)

A function for string-escaping text.

<?: write('const text = "' + stringEscape('Some"text') + '";'); ?>
<?= text ?>
  • str <string> - Any text to string-escape.
  • returns <string> - The escaped text.

context.filename

The filename of the current template if the filename compile option is set, otherwise null.

context.dirname

The directory name of the current template if the filename compile option is set, otherwise null.

node globals

Some node globals are also available from compile-time code, but they are not part of the context object.

  • Buffer
  • setImmediate, clearImmediate
  • setInterval, clearInterval
  • setTimeout, clearTimeout
  • console
  • global
  • process

Compile-time extensions

The compile time context can be extended from the compilation api using the extend compile option. The extend option specifies a function that is called for each compile-time context before a template is compiled. From this you can extend the context itself:

const template = await compile('<?: example(); ?><?= magic ?>', {
    extend(context) {
        // When calling the example function from compile-time code
        // it will embed a constant into the template's runtime code:
        context.example = () => {
            context.write('const magic = 42;');
        };

        // You can also call compile time api from here which behaves
        // like it has been called from the beginning of the template.
        context.embedObject('someEmbeddedData', {foo: 'bar'});
    }
});

template(); // -> 42

Caching compiled templates

Caching compiled template code can speed up compilation of templates with includes. To take advantage of caching you have to specify the cache compile option.

const cache = new Map()

const template = await compile('...', {cache})

Note that the same cache map can be used for multiple compilations that use the same compile options.

Custom syntax

Custom syntax can be defined using the syntax compile option. The syntax option for the default syntax would look like this:

syntax: {
    // Variable length:
    open: '<?',
    close: '?>',

    // Fixed length:
    compilerControl: ':',
    writeEscaped: '=',
    writeUnescaped: '-',
    comment: '#',
    escape: open[open.length - 1]
}

Note that a custom syntax configuration is not validated. To ensure that everything will work as expected, the following guidelines should be followed:

  • All options have a different value.
  • Variable length options have a length of at least 1.
  • Fixed length options have a length of 1.

Async templates

Asynchronous templates can be compiled by setting the async option.

const template = await compile('...', {async: true});

// Render an async template:
const html = await template(locals);

In async templates you can use await:

<?= await locals.doSomething() ?>

Note that included or embedded templates will not be async. You have to set the async option for each included or embedded template manually if you want them to be async too!

Compile raw function code

Sometimes, you need access to the raw compiled template function code.

const code1 = await compile.code(src, options);
const code2 = await compile.fileToCode(filename, options);
  • src, filename and options arguments are the same as in compile and compile.file
  • returns <string> - The compiled template function code.

Compiled functions consist of a minified arrow function (that may be async depending on your options) that could look like the following:

(locals={})=>{let __r='';__r+='Hello '+__e( name )+'!';return __r;}
  • __r - Variable used to assemble the template output.
  • __e - Function used to html-escape output data. When using compiled function code, you may want to provide this function from outer scope.

Compile template modules

Templates can also be compiled into javascript modules.

const code1 = await compile.toModule(src, options);
const code2 = await compile.fileToModule(filename, options);
  • src, filename and options arguments are the same as in compile and compile.file
  • returns <string> - The compiled module code.

The compiled modules require the escape-html module for html-escaped output. If subcode is not in your dependencies you should npm install escape-html!

Module types

The module type can be set using the moduleType compile option:

moduleTypeOutput
'common'Outputs a CommonJS module.
'es15'Outputs a module using es2015 import statements.
const code = await compile.toModule(src, {moduleType: 'es15'});

Parser API

The parser api gives access to the internal parser of subcode.

const {parse} = require('subcode');

parse(src, output, syntax);
  • src <string> - The template code.
  • output <object> - The parser output that has to implement several output functions. The output functions are called while parsing the template:
    • plain(html) - Called with html code to output.
    • compilerControl(js) - Called with compile-time code.
    • writeEscaped(js) - Called with runtime code to output html-escaped.
    • writeUnescaped(js) - Called with runtime code to output unescaped.
    • control(js) - Called with runtime control code.
  • syntax <object> - An optional syntax configuration as described above.

Development notes

Running tests

npm test

Running tests while developing

npm run dev

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100