• Stars
    star
    144
  • Rank 255,590 (Top 6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 13 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A template engine that works the way you expect.

Combyne

Stable: 2.0.0

No dependencies. Can be loaded as a browser global, AMD module, and Node module. Works with Browserify. Can be installed via NPM or Bower.

Combyne works great with:

Install.

Node:

npm install combyne

Bower:

bower install combyne

Getting started.

Node.

Require in your source:

var combyne = require("combyne");

AMD.

// Configure the path if necessary.
require({
  paths: {
    combyne: "path/to/combyne"
  }
});

// Use in a module.
define(["combyne"], function(combyne) {});

There is also an AMD plugin for easier consumption and building:

https://github.com/tbranyen/combyne-amd-loader

Browserify.

combynify is a browserify transform plugin to pre-compile combyne templates.

In your code:

var template = require("./template.combyne");
var data = { ... }

template.render(data)

Install combynify and browserify it:

npm install --save-dev combynify
browserify -t combynify main.js > bundle.js

Once the template is precompiled, there is no dependency on the combyne engine.

Browser global.

Include the latest stable in your markup:

<script src="path/to/dist/combyne.js"></script>

Compatibility.

Combyne is written in ES5 and contains polyfills to provide support back to IE 7. These polyfills are omitted in the dist/combyne.js file, but exist in the dist/combyne.legacy.js file. Use this if you are developing/testing with older IE.

Basic usage.

var tmpl = combyne("hello {{msg}}!");
tmpl.render({ msg: "world" });

// => hello world!

Features.

Combyne works by parsing your template into an AST. This provides mechanisms for intelligent compilation and optimization. The template is converted to JavaScript and invoked upon calling render with data.

Security.

By default all templates are encoded to avoid possible issues arising from XSS attacks. This is specifically applied to properties and you can avoid this by using the raw property style: {{{ value }}}. This is very similar to Mustache.

While using this template engine in the browser, it is important to note that you should not trust unknown values to render unencoded. The recommendation is to forget it exists while writing templates in the browser, unless you know what you're doing and have a valid use case.

View this XSS (Cross Site Scripting) Prevention Cheat Sheet for more information.

Comments.

Comments are useful for ignoring anything between the open and close. They can be nested.

var tmpl = combyne("test {%-- not parsed --%}");
tmpl.render();

// => test

Custom delimiters.

If you are not happy with the default Mustache-like syntax, you can trivially change the delimiters to suit your needs. You may only change the delimiters at a global level, because templates are compiled immediately after invoking the combyne function.

// This sets the delimiters, and applies to all templates.
combyne.settings.delimiters = {
  START_PROP: "[[",
  END_PROP: "]]"
};

var tmpl = combyne("[[msg]]", { msg: "hello world" });

tmpl.render();
// => hello world

Defaults:

START_RAW:  "{{{"
END_RAW:    "}}}"
START_PROP: "{{"
END_PROP:   "}}"
START_EXPR: "{%"
END_EXPR:   "%}"
COMMENT:    "--"
FILTER:     "|"

Template variables.

var template = "{{foo}}";
var context = { foo: "hello" };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "hello"

Variables can be literal values, functions, or even objects.

Passing arguments to a function.

var template = "{{toUpper 'hi'}}";
var context = { toUpper: function(val) { return val.toUpperCase(); } };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "HI"

Using filters on variables.

var template = "{{foo|reverse}}";
var context = { foo: "hello" };

var tmpl = combyne(template);

tmpl.registerFilter("reverse", function(val) {
  return val.split("").reverse().join("");
});

var output = tmpl.render(context);
/// output == "olleh"

Passing arguments to filters.

You may find that the property value is not enough information for the filter function, in which case you can send additional arguments.

var tmpl = combyne("{{ code|highlight 'javascript' }}");

tmpl.registerFilter("highlight", function(code, language) {
  // Magic highlight function that takes code and language.
  return highlight(code, language);
});

Chaining filters on variables.

var template = "{{foo|reverse|toUpper}}";
var context = { foo: "hello" };

var tmpl = combyne(template);

tmpl.registerFilter("reverse", function(val) {
  return val.split("").reverse().join("");
});

tmpl.registerFilter("toUpper", function(val) {
  return val.toUpperCase();
});

var output = tmpl.render(context);
/// output == "OLLEH"

Conditionals.

Instead of being logic-less, combyne doesn't make any assumptions and allows you to do things like if/elsif/else with simple conditionals, such as if something == somethingElse or if not something. All data types will be coerced to Strings except for Numbers.

var template = "{%if not foo%}why not?{%endif%}";
var context = { foo: false };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "why not?"

or a more complicated example...

var template = "{%if foo == 'hello'%}Hi!{%else%}bye...{%endif%}";
var context = { foo: "hello" };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "Hi!"

elsif is also supported:

var template = "{%if foo == ''%}goodbye!{%elsif foo == 'hello'%}hello!{%endif%}";
var context = { foo: "hello" };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "hello!"

You can also pass conditionals through filters to do more complex logic:

var tmpl = combyne("{%if hello|upper|reverse == 'OLLEH'%}hello{%endif%}");

tmpl.registerFilter('upper', function(value) {
  return value.toUpperCase();
});

tmpl.registerFilter("reverse", function(value) {
  return value.split("").reverse().join("");
});

var output = tmpl.render({ hello: 'hello'});
/// output == "hello"

It also works with properties that need to be not encoded

var tmpl = combyne("{%if {{{hello}}} == '<>'%}hello{%endif%}");

var output = tmpl.render({ hello: '<>'});
/// output == "hello";

Iterating arrays.

Also works on array-like objects: arguments and NodeList.

var template = "{%each foo%}{{.}} {%endeach%}";
var context = { foo: [1,2,3,4] };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "1 2 3 4 "

You can also pass the value into a filter before iterating over it

var template = "{%each foo|upper%}{{.}} {%endeach%}";
var context = { foo: ["a", "b", "c"] };

template.registerFilter("upper", function(array) {
  return array.map(function (entry) {
    return entry.toUpperCase();
  });
});

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "A B C"

You can even use filters on the root object by either specifying '.' or leaving it blank

var template = "{%each .|upper%}{{.}} {%endeach%}";
var context = ["a", "b", "c"];

template.registerFilter("upper", function(array) {
  return array.map(function (entry) {
    return entry.toUpperCase();
  });
});

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "A B C"

Iterating an array of objects shorthand.

If you pass an array of objects to Combyne, you may iterate it via a shorthand:

var template = "{%each%}{{foo}} {%endeach%}";
var context = [{ foo: 1 }, { foo: 2 }, { foo: 3 }, { foo: 4 }];

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "1 2 3 4 "

Change the iterated identifer within loops.

var template = "{%each arr as val%}{{val}}{%endeach%}";
var context = { arr: [1,2,3] };

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output = "123"

Iterating objects.

var template = "{%each fruits as val key%}the {{key}} is {{val}}{%endeach%}";
var context = {
  fruits: {
    apple: "green"
  }
};

var tmpl = combyne(template);

var output = tmpl.render(context);
/// output == "the apple is green"

Partials.

var template = "{{foo}} {%partial bar%}";
var context = { foo: "hello" };

var tmpl = combyne(template);

tmpl.registerPartial("bar", combyne("{{name}}", {
  name: "john"
}));

var output = tmpl.render(context);
/// output == "hello john"

Pass template data to partial.

If you need to pass the template's data to the partial, simply use the magic operator ..

var template = "{{foo}} {%partial bar .%}";
var context = { foo: "hello", name: "carl" };

var tmpl = combyne(template);

tmpl.registerPartial("bar", combyne("{{name}}"));

var output = tmpl.render(context);
/// output == "hello carl"

If you need to manipulate the data passed to any partial, you must create a function on the parent template's data that returns an object or array that will be used by the nested partial.

You can even pass arguments along to that function to use.

An example follows:

var template = "{%partial bar showName 'carl'%}";
var context = {
  showName: function(name) {
    return { displayName: name };
  }
};

var tmpl = combyne(template);

tmpl.registerPartial("bar", combyne("hello {{displayName}}"));

var output = tmpl.render(context);
/// output == "hello carl"

If you wish to filter the data passed to the partial you can supply a filter.

var people = { carl: { knownAs: 'Carl, the Duke' } };
var template = "{%partial bar people|find 'carl'%}";
var context = {
  find: function(name) {
    return people[name];
  }
};

var tmpl = combyne(template);

tmpl.registerPartial("bar", combyne("hello {{knownAs}}"));

var output = tmpl.render(context);
/// output == "hello Carl, the Duke"

Template inheritance.

When using a framework that handles rendering for you and you wish to inject your template into a different template (maybe a layout) in a given region you can express this through template inheritance expressions.

Illustrated below is a typical use case for this feature:

var template = "{%extend layout as content%}<h1>{{header}}</h1>{%endextend%}";
var context = { header: "Home page" };

var page = combyne(template);

// Register the layout template into the page template.
page.registerPartial("layout", combyne("<body>{%partial content%}</body>"));

var output = page.render(context);
/// output == "<body><h1>Home page</h1></body>"

The context object you pass at the page.render line will be propagated to the partial template. This means that you can optionally pass a nested object structure like:

var context = {
  header: "My site",

  page: {
    header: "Home page"
  }
};

// Pass the page object to the page template, restricting what it has access
// to.
var layout = "<title>{{header}}</title><body>{%partial content page%}</body>";

// Register it in the partial.
page.registerPartial("layout", combyne(layout));

var output = page.render(context);
/// output == "<title>My site</title><body><h1>Home page</h1></body>"

Unit tests.

There are many ways to run the unit tests as this library can operate in various environments.

Browser

Open test/index.html in your web browser.

Node

Run the tests inside the Node runtime and within PhantomJS:

grunt test

This will run the tests against the AMD source, the built modern dist/combyne.js, and the built legacy dist/combyne.legacy.js files.

Continuous testing

To keep the PhantomJS tests running continuously, run:

grunt karma:watch

The tests will automatically run whenever files change.

Code coverage

If you run the tests through Karma, a test/coverage directory will be created containing folders that correspond with the environment where the tests were run.

If you are running the defaults you should see something that looks like:

.
└── coverage
    ├── Chrome 33.0.1750 (Linux)
    └── PhantomJS 1.9.7 (Linux)

Inside PhantomJS contains the HTML output that can be opened in a browser to inspect the source coverage from running the tests.

More Repositories

1

backbone-boilerplate

A workflow for building Backbone applications.
JavaScript
4,496
star
2

backbone.layoutmanager

UNMAINTAINED 7/31/18: A layout and template manager for Backbone applications.
JavaScript
1,681
star
3

diffhtml

diffHTML is a web framework that helps you build applications and other interactive content
JavaScript
869
star
4

hyperlist

A performant virtual scrolling list utility capable of rendering millions of rows
JavaScript
407
star
5

github-viewer

GitHub Viewer.
JavaScript
370
star
6

use-amd

An AMD plugin for consuming globally defined JavaScript.
JavaScript
271
star
7

backbone.routemanager

Better route management for Backbone.js projects.
JavaScript
111
star
8

salita

Automatically upgrade all NPM dependencies.
JavaScript
95
star
9

babel-plugin-transform-commonjs

A Babel 7 plugin transform to convert CommonJS into ES Modules
JavaScript
82
star
10

lodash-template-loader

A Lo-Dash template loader plugin for AMD projects.
JavaScript
54
star
11

backbone.cacheit

Fetch caching made super simple in Backbone.
JavaScript
47
star
12

vertebrae

Backbone transport management.
JavaScript
43
star
13

previewcode

previewcode.com public repository.
JavaScript
32
star
14

nodewii

Node.js cwiid asynchronous native bindings
C++
31
star
15

growing-up-with-backbone

Growing Up With Backbone.
JavaScript
28
star
16

localhub

A local hub of your Git repositories
JavaScript
15
star
17

redux-create-action-types

Easily create immutable, strict, and well formed action types
JavaScript
15
star
18

diffhtml-prollyfill

DEPRECATED MOVED TO MAIN REPO
14
star
19

scopedcss

Scoping your CSS to a specific selector or element.
JavaScript
14
star
20

nodefreckle

Node.js freckle api bindings
JavaScript
13
star
21

pigeonpost

Amazon SES E-Mail Scheduler & Delivery API
JavaScript
11
star
22

vim-typescript

VIM TypeScript Plugin
JavaScript
10
star
23

site-content

All site content.
JavaScript
10
star
24

hooks-theme-refs

A simple pattern for well structured React Components
JavaScript
10
star
25

todomvc

TodoMVC implemented with VDOM, Tagged templates, and Redux
JavaScript
8
star
26

talks

My Talks
JavaScript
8
star
27

diffhtml-inline-transitions

Inline transition hooks directly into tagged templates
JavaScript
7
star
28

miso.fs

A Miso Dataset FileSystem Importer.
JavaScript
7
star
29

cat-api-boilerplate

A starting point for creating your very own cat API.
JavaScript
7
star
30

react-babel-esm

Using React and Hooks without a bundler, from npm, and with native browser ESM
JavaScript
6
star
31

react-hyperlist

A React wrapper for HyperList
JavaScript
6
star
32

redux-simple-routing

Simple routing for React/Redux apps
JavaScript
5
star
33

babel-plugin-resolve-imports-for-browser

A Babel plugin to convert imports to support browser ESM
JavaScript
5
star
34

bserve

Static HTTP server which automatically transpiles JS using Babel
JavaScript
4
star
35

parallel-run

Run npm scripts multiplexed
JavaScript
4
star
36

babel-preset-browser-esm

Babel preset to make running JavaScript in the browser easier
TypeScript
4
star
37

lm-forms

Backbone LayoutManager and Backbone Forms integration
JavaScript
4
star
38

devoxx-2013

Backbone and Tools
JavaScript
4
star
39

app.toweatherproof.me

Basic Weather App.
JavaScript
4
star
40

chart-component

An experimental Web Component that renders a chart, using diffHTML
JavaScript
4
star
41

babel-ast-cache-perf-tests

Benchmarks various ways of getting a Babel AST
JavaScript
3
star
42

tbranyen.com

My personal site.
JavaScript
3
star
43

grunt-nodequnit

Run QUnit tests in the Node.js environment.
JavaScript
3
star
44

transform-tagged-diffhtml

A Babel plugin transform for diffHTML tagged template strings
JavaScript
3
star
45

diffhtml-synthetic-events

Swaps out inline events for synthetic event delegation.
JavaScript
3
star
46

synchronizer

Build AMD projects into a single UMD file.
JavaScript
3
star
47

consumare

Consume content from Git.
JavaScript
3
star
48

combyne-amd-loader

A Combyne template loader plugin for AMD projects.
JavaScript
3
star
49

combynexpress

Combyne view engine for Express.
JavaScript
3
star
50

extless-loader

Loads Node ESM in a more compatible way by making extensions optional.
JavaScript
3
star
51

d3-playground

D3 playground with webpack hot reloading & babel for prototyping
JavaScript
3
star
52

cropcircle

Pattern framework for building web sites and applications
HTML
2
star
53

quick-dom-node

Make DOM Nodes in Node quickly and they are also quick
JavaScript
2
star
54

make8bitart

an in-browser canvas tool which is great fun!
JavaScript
2
star
55

diffhtml-snowpack

Extremely simple demo using diffHTML and Snowpack
JavaScript
2
star
56

amd.js

Experimental AMD loader
JavaScript
2
star
57

static-sync

A static server that automatically keeps HTML files in sync using Virtual DOM.
JavaScript
2
star
58

awesome-config

My minimalistic Awesome WM configuration.
Lua
2
star
59

diffhtml-logger

DEPRECATED MOVED TO MAIN REPO
2
star
60

LiveEdit-CSS

JavaScript
2
star
61

amd-test-bed

A project test bed
JavaScript
2
star
62

turbopack-webpack-example

Example of turbopack integration with webpack
JavaScript
1
star
63

amd-next

If we were to have a v2 of the AMD specification what would it look like?
1
star
64

Charcoal_UI_v1.1

MIRROR of Charcoal UI 1.1
Lua
1
star
65

calendar.js

A long lost project
JavaScript
1
star
66

load-as-fake-workers

Loads scripts as fake workers for testing
JavaScript
1
star
67

ayo-node-esm-tests

Testing Node vs Ayo ES Modules
JavaScript
1
star
68

broken-npm-resolution

Works with updates to react-blessed-you (fork for react-blessed)
JavaScript
1
star
69

diffhtml-esm-test-app

Zero build test of diffHTML, Redux, and a simple event emitter
JavaScript
1
star
70

css-loader

CSS Loader.
JavaScript
1
star
71

cribbage

Follow me as I get incrementally updated from College-standards to Modern-standards.
JavaScript
1
star