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

fleau

v16.2.0

Published

An extensible, readable, streamed, safe templating language.

Downloads

4,261

Readme

Fléau Build Status

An extensible, readable, streamed, safe templating language.

Taste

npm install fleau
var fleau = require('fleau');
var fs = require('fs');
fleau(fs.createReadStream('./YourTemplateFile'),   // input
      process.stdout,                              // output
      {data: 'to use', "in": ['your', 'file']});   // data

Example templates.

Thaddee {{if {{apostles.indexOf(thaddee) != -1}}
          then was else wasn't}} an apostle

{
  thaddee:  'Thaddaeus',
  apostles: ['Simon','Andrew','James','John','Philip','Bartholomew',
             'Matthew','Thomas','James','Simon','Judas','Judas']
}

Thaddee wasn't an apostle

Loops:

Characters:
{{for i, guy in protagonists
{{ {{= i in plain}}. {{= guy in plain}}
}} }}

{
  protagonists: ['Blondie', 'Angel', 'Tuco']
}

Characters:
 0. Blondie
 1. Angel
 2. Tuco

Whatever comes directly after {{ (without whitespace) is a macro. For instance, for and = are macros. (Go here for a full list.) If there is no macro specified, it includes arbitrary JS, which gives a different style to the template language:

<table>
  <tr>
    <th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th>
    <th>Sat</th><th>Sun</th>
  </tr>
  {{ let days = daysForMonth(month) }}
  {{ for (let i = 7, j = 0; i < days.length; i += 7) { }}
    <tr>
      {{ for (; j < i; j++) { }}
        <td>{{= days[j] in html}}</td>
      {{ } }}
    </tr>
  {{ } }}
</table>

Manual

Exports

The exported object is a function that takes four parameters:

fleau(inputStream,         // a template
      outputStream,        // where to output the result
      {data: 'to use'},    // data literal
      function callback(error) {…});

Each parameter is accessible as a variable in the template. More on that later.

This exported function also has a series of entries.

  • fleau.create(string) returns a function(scope) that takes a JSON object, and returns a readable stream that is the result of applying that scope to the template. This function can throw, as well as the function it returns.
  • fleau.template(string) returns a function(writer, scope, end) that takes a writer (a function that takes a string) and a scope (a JSON object), and writes the result of applying that scope to the template. end() is a function called once the whole template is produced. This lets you avoid re-compiling the template every time.
  • fleau.sandboxTemplate(string) returns a function that takes a writer (a function that takes a string), a scope (a JSON object), a timeout in milliseconds, and a callback that takes an error. It writes the result of applying that scope to the template.
  • fleau.clear() destroys background processes if you used sandboxTemplate.
  • fleau.parsers is a map of all parser functions. Parsers are used in the = macro. Each is a function from string to string.
  • fleau.macros is a map of all macro functions. A macro is what specifies what the control zone does (more on this below). This is exported for extensibility purposes. Each function takes a list of parameters and returns a string containing JS code, where $_parsers is fleau.parsers, $_write is a function outputting the string given as a parameter, and $_scope is a map from all variables defined in the scope to their values. You may use fleau.compile below.
  • fleau.compile takes a string template and returns the code as a string of the contents of a function that uses $_write, $_scope and $_parsers as seen previously.

Macros

In a control zone (a zone in the template between {{ and }}), you have a series of textual parameters, either delimited by whitespace, or by {{…}}. The first of those parameters selects a macro. Macros contain instructions to output data in the template. The following are built-in macros.

The = macro displays an expression and escapes it using a parser.

Here be dragons: {{= data in {{json 2}} }}.

{ data: ['T-O Psalter world map', 'Borgia map', 'Fra Mauro Map'] }

Here be dragons: ['T-O Psalter world map', 'Borgia map', 'Fra Mauro Map'].

You can have parameters to parsers (more below), and you can also chain them using a sequence of in parser instructions.

Conditions: the if macro.

I am {{if here then {{here. Hello!}} else if atWork then
{{at work…}} else out.}} Anyway, how do you do?

{ here: false, atWork: true }

I am at work… Anyway, how do you do?

(You can have as many else if as you want.)

Loops: the for macro. You have two forms: with the index, and without. We have already seen with the index in the intro.

Characters:
{{for guy in protagonists
{{- {{= guy in plain}}
}} }}

{
  protagonists: ['Blondie', 'Angel', 'Tuco']
}

Characters:
- Blondie
- Angel
- Tuco

The comment macro, #, used if you want to disable a control zone without removing it.

Here be {{# catburger!}}

{}

Here be 

You can also extend the macro system with additional macros. For the purpose of the example, let's write a macro that joins a list together.

fleau.macros['join'] = function(params) {
  var list = params[0];
  var sep = params[2];  // Leave a param for `with`
  var code = '$_write($_scope[' + JSON.stringify(list) + ']' +
    '.join(' + JSON.stringify(sep) + '));\n';
  return code;
};
I love {{join kids with {{, }}}}.

Assuming the scope contains {kids: ['Jack', 'Hugh', 'Hector']}:

I love Jack, Hugh, Hector.

Macro parameters that accept keys from the literal can generally accept JS expressions, in which you can use the literal's keys as JS variables.

Parsers

  • plain: nothing is escaped.
  • html: HTML escapes (such as <, >, &`, etc.).
  • xml: similar to HTML.
  • xmlattr: escapes for an XML attribute (such as ' to &apos;).
  • jsonstring: inside a JS (or JSON) string, escapes ', \, etc.
  • json: outputs a JSON representation of the input. This parser has the usual parameters given to JSON.stringify.
  • uri: escapes spaces to %20, etc, as done in URIs.
  • !uri: unescapes URIs.
  • integer: outputs the data as an integer.
  • intradix: outputs an integer using the radix given as a parameter.
  • float: outputs a floating-point number using the number of digits after the comma as given as a parameter.
  • exp: outputs a number in scientific notation, using the number of digits after the comma as given as a parameter.

Dev intro

The entirety of the templating system is hold in the fleau.js code file. There are tests in the test/ directory, which you run using make test.

fleau.js starts with a series of helper functions which are really meant to work together. The main data structures, ControlZone and TopLevel, contain all information about the location of control zones and escapes in and out of them.

The data is constructed by the toplevel function. It doesn't do any string manipulation; it only stores the indices of interesting spots. It is applied not to the whole document, but to the start of the template until the end of the first control zone it finds. That way, data starts being sent down the wire long before all the document has been processed, which is useful in huge documents.

Quite obviously, the zoneParser function operates on each control zone and separates it into space-separated tokens, or longer phrases. Then, escapeCurly substitutes escapes to their corresponding output.

The format function is obviously the main entry point, and the main thing it does is call formatString, giving it the complete contents of the template. In turn, formatString reads each control zone sequentially, and calls the corresponding macro every time.

All macros are in the macros map; each macro name is associated to the macro function. Similarly, parsers are in the parsers map; each parser name is associated to the corresponding parser function, which is given the text to parse and optional parser parameters.

One last thing. If you are wondering about the peculiar code style, it is an experiment in readability. Put a space between the function and the parameter list if you want to contribute.

License

LGPL, see LICENSE.