Embedded CoffeeScript templates

Overview

Eco: Embedded CoffeeScript templates

Eco lets you embed CoffeeScript logic in your markup. It's like EJS and ERB, but with CoffeeScript inside the <% ... %>. Use it from Node.js to render your application's views on the server side, or compile your templates to JavaScript with the eco command-line utility and use them to dynamically render views in the browser.

Here's how an Eco template looks:

<% if @projects.length: %>
  <% for project in @projects: %>
    <a href="<%= project.url %>"><%= project.name %></a>
    <p><%= project.description %></p>
  <% end %>
<% else: %>
  No projects
<% end %>

Usage

Use eco.render() to render your templates. The first argument is the template source as a string. The second argument is the context object which contains your view state and any helper methods you want to call.

eco = require "eco"
fs  = require "fs"

template = fs.readFileSync __dirname + "/views/projects.html.eco", "utf-8"
console.log eco.render template, projects: [
  { name: "Mobile app", url: "/projects/1", description: "Iteration 1" },
  { name: "Home page redesign", url: "/projects/2" }
]

Eco is fully synchronous. If your template needs to access data from asynchronous operations, perform those first before calling render.

Language reference

Eco's syntax is simple:

  • <% expression %>: Evaluate a CoffeeScript expression without printing its return value.
  • <%= expression %>: Evaluate a CoffeeScript expression, escape its return value, and print it.
  • <%- expression %>: Evaluate a CoffeeScript expression and print its return value without escaping it.
  • <%= @property %>: Print the escaped value of the property property from the context object passed to render.
  • <%= @helper() %>: Call the helper method helper from the context object passed to render, then print its escaped return value.
  • <% @helper -> %>...<% end %>: Call the helper method helper with a function as its first argument. When invoked, the function will capture and return the content ... inside the tag.
  • <%% and %%> will result in a literal <% and %> in the rendered template, respectively.

A note about whitespace

CoffeeScript is whitespace-sensitive, but your templates aren't. Therefore, Eco code tags that begin an indented CoffeeScript block must be suffixed with a colon. To indicate the end of an indented block, use the special tag <% end %>. For example:

<% if @project.isOnHold(): %>
  On Hold
<% end %>

You don't need to write the if and end tags on separate lines:

<% if @project.isOnHold(): %> On Hold <% end %>

And you can use the single-line postfix form of if as you'd expect:

<%= "On Hold" if @project.isOnHold() %>

Certain forms in CoffeeScript, such as else, must be unindented first. Eco handles that for you automatically:

<% if @project.isOnHold(): %>
  On Hold
<% else if @project.isArchived(): %>
  Archived
<% end %>

The context object

The context object you pass to eco.render() becomes the value of this inside your template. You can use CoffeeScript's @ sigil to easily access properties and call helper methods on the context object.

eco.render "<p><%= @description %></p>",
  description: "HTML 5 mobile app"

Helpers

Helper methods on your context object can access other properties on the context object in the same way they're accessed in the template: through this, or with the @ sigil.

translations = require "translations"

eco.render "<span><%= @translate 'common.welcomeText' %></span>",
  language:  "en"
  translate: (key) ->
    translations[@language][key]

Escaping and unescaping

When you print an expression in a template with <%= ... %>, its value is HTML-escaped. For example,

eco.render "<%= @description %>",
  description: "<strong>HTML 5</strong> mobile app"

would render:

&lt;strong&gt;HTML 5&lt;/strong&gt; mobile app

You can use the <%- ... %> tag to print the value of an expression without escaping it. So this code:

eco.render "<%- @description %>",
  description: "<strong>HTML 5</strong> mobile app"

would produce:

<strong>HTML 5</strong> mobile app

It is sometimes useful to generate markup in helper methods. The special safe method on the context object tells Eco that the string can be printed in <%= ... %> tags without being escaped. You can use this in conjunction with the context object's escape method to selectively sanitize parts of the string. For example,

eco.render "<%= @linkTo @project %>",
  project: { id: 4, name: "Crate & Barrel" }
  linkTo: (project) ->
    url  = "/projects/#{project.id}"
    name = @escape project.name
    @safe "<a href='#{url}'>#{name}</a>"

would render:

<a href='/projects/4'>Crate &amp; Barrel</a>

Custom escape helpers

By default, Eco's escape method takes a string and returns an HTML-escaped string. You can override this behavior to escape for formats other than HTML, or to bypass escaping entirely. For example,

eco.render "From: <%= @address %>",
  address: "Sam Stephenson <[email protected]>"
  escape:  (string) -> string

would return:

From: Sam Stephenson <[email protected]>

Blocks and capturing

You can capture blocks of a template by wrapping them in a function definition. For example, rendering this template:

<% div = (contents) => %>
   <div><%- contents %></div>
<% end %>
<%= div "Hello" %>

would produce:

<div>Hello</div>

Captured blocks can be passed to helper methods too. In this example, the capture body is passed to the formFor helper as its last argument. Then the formFor helper calls this argument to produce a value.

template = """
  <%= @formFor @project, (form) => %>
    <label>Name:</label>
    <%= form.textField "name" %>
  <% end %>
"""

eco.render template,
  project: { id: 1, name: "Mobile app" }
  formFor: (project, yield_to) ->
    form =
      textField: (attribute) =>
        name  = @escape attribute
        value = @escape @project[attribute]
        @safe "<input type='text' name='#{name}' value='#{value}'>"

    url  = "/projects/#{@project.id}"
    body = yield_to form
    @safe "<form action='#{url}' method='post'>#{body}</form>"

Note: In general, you should use CoffeeScript's fat arrow (=>) to define capturing functions, so that you have access to the context object inside the captured block. Treat the plain arrow (->) as an optimization, for when you are certain the capture body will not need to reference properties or helper methods on the context object.

Contributing

You can check out the Eco source code from GitHub:

$ git clone http://github.com/sstephenson/eco.git

To run Eco's test suite, install nodeunit and run cake test.

Report bugs on the GitHub issue tracker.

License (MIT)

Copyright (c) 2010 Sam Stephenson [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Special thanks

Comments
  • Line Break / Indentation Error in Windows

    Line Break / Indentation Error in Windows

    Due to some restrictions on my current project I am forced to develop in a windows environment, this being said I have come across a windows specific parsing issue. When I run Eco.compile(File.read ("ANY FILE WITH A CONTROL STRUCTURE") ) I get an unexpected indent error, when I run this same command on my mac it compiles properly. The only control structure that doesn't error out is an if statement without an else statement. I am guessing that this is an issue with the windows line breaks causing the parser to improperly indent, but this is only a guess.

    opened by bmarcaur 15
  • Dist task in Cakefile is outdated

    Dist task in Cakefile is outdated

    In the Cakefile, the dist task refers to files that look like they moved.

    lib/eco.js doesn't exist, and lib/eco/compiler.coffee, et. al should probably be src/eco/compiler.coffee

    bug fixed 
    opened by milaniliev 11
  • Support Node.js Express?

    Support Node.js Express?

    Hey there,

    Great work on this stuff! Just one request: is it possible to use this as a template engine in Express?

    I'm not sure exactly what that entails, but I don't think it's much; I'm pretty sure it's just a matter of exposing the expected APIs. I can ask the Express author(s) for details on this if you would like.

    Here's the sample app I made to test this: https://gist.github.com/927144 (Check out the comments to see the error message.)

    Thanks in advance!

    Cheers, Aseem

    opened by aseemk 9
  • Escape backbone model attributes causes error

    Escape backbone model attributes causes error

    I've been trying out using stitch.js and eco with backbone.js but I'm having some troubles.

    I'm sending a backbone model to a Eco template with:

    $(this.el).html(template(this.model));
    

    Then in my template, all I'm trying is this:

    <%= @get('text') %>
    

    Now what I've been able to figure out is that eco first checks if there's already an escape function on the object in question and since it's a backbone model there is a method called escape so eco calls that method instead of its own. The backbone escape method looks likes this:

    escape : function(attr) {
        var html;
        if (html = this._escapedAttributes[attr]) return html;
        var val = this.attributes[attr];
        return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val);
       },
    

    Now since when this is called this is DOMWindow the this._escapedAttributes[attr] part fails with an error:

    Uncaught TypeError: Cannot read property 'Finish some great tasks' of undefined
    _.extend.escape
    __sanitize
    __obj.safe
    __obj.safe
    module.exports
    module.exports.Backbone.View.extend.render
    module.exports.Backbone.View.extend.add
    _.each._.forEach
    _.each.Backbone.Collection.(anonymous function)
    module.exports.Backbone.View.extend.addAll
    Backbone.Events.trigger
    _.extend.reset
    _.extend.fetch.options.success
    jQuery.Callbacks.fire
    jQuery.Callbacks.self.fireWith
    done
    jQuery.ajaxTransport.send.callback
    

    'Finish some great tasks' is what's in my models 'text' attribute.

    I tought maybe I could change __esacpe(value) in your code to __escape.call(__obj, value) but I havn't got it working yet. Any ideas?

    Also I can change the template to <%- @escape('text') %> and it works but I'm not sure what I think is best and I thought I would tell you about this problem.

    opened by marlun 4
  • Multi-line code blocks

    Multi-line code blocks

    It's pretty painful not being able to use newlines inside CoffeeScript code blocks!

    Here's a simple example test page showing the variety of use cases I miss from other templating engines (like EJS):

    https://gist.github.com/966202

    Thanks for your consideration. =)

    opened by aseemk 4
  • Getting data associated with a rendered template element

    Getting data associated with a rendered template element

    Finding data associated with elements is a rather common use case. For example, let's say an element was clicked on, I'd love to be able to retrieve the data the element is associated with in the event handler. This is possible in jQuery.tmpl using $.fn.tmplItem(). Does eco have support, or plans for such a feature?

    opened by maccman 3
  • Client-side inline CoffeeScript

    Client-side inline CoffeeScript

    Just an idea / feature request:

    In general, you want client-side JavaScript in a separate, external file -- good for cacheability, separation of concerns, etc.

    There are occasions though where you want a snippet of inline JavaScript in a <script> block. Maybe it's something page-specific, or something dynamically generated, or maybe this is just a quick prototype.

    It would be super awesome and convenient if I could write that code in CoffeeScript, e.g. via <script type="text/coffeescript">, and have Eco automatically compile that to JavaScript. <3

    opened by aseemk 3
  • Can't build latest code using cake

    Can't build latest code using cake

    Something seems to be wrong with Cakefile. That's what I get:

    [email protected] ~/opensource/eco $ ~/bin/cake dist
    
    fs:153
      return binding.open(path, stringToFlags(flags), mode);
                     ^
    Error: ENOENT, No such file or directory '/home/rainman/opensource/eco//home/rainman/opensource/eco/package.json'
        at Object.openSync (fs:153:18)
        at Object.readFileSync (fs:94:15)
        at /home/rainman/opensource/eco/Cakefile:44:17
        at Object.action (/home/rainman/opensource/eco/Cakefile:49:26)
        at /home/rainman/.node_libraries/.npm/coffee-script/0.9.5/package/lib/cake.js:31:26
        at /home/rainman/.node_libraries/.npm/coffee-script/0.9.5/package/lib/cake.js:53:23
        at path:81:19
        at node.js:773:9
    
    bug fixed 
    opened by kyegupov 3
  • Can't enclose eco expression within angle brackets

    Can't enclose eco expression within angle brackets

    E.g. if I write <<%= @email %>> then nothing is printed.

    This does work < <%= @email %> > but isn't what I want.

    I'm assuming this is a bug? Is there a way to escape "special" characters?

    opened by KyleAMathews 2
  • Helper functions that aren't tied to a model

    Helper functions that aren't tied to a model

    A nice addition would be helper functions that aren't tied to the model. One example would be a function to localize time (see below). If I've understood things correctly that would now require the localizedTime function on each model. If view/template helpers could be passed as as third argument that would be neat.

    Or perhaps this is already possible, and I've just missed it.

    var localizedTime = function(utcTime) {
        var offset = (new Date).getTimezoneOffset() / -60;
        // return a localized time (utcTime + offset)
    } 
    
    opened by sandstrom 2
  • Property `compiler` to easily use eco with express

    Property `compiler` to easily use eco with express

    I've added a property to eco so you can declare it as a express compiler like this:

    express = require 'express'
    eco = require 'eco'
    
    app = express.createServer()
    
    app.configure ->
      app.register '.eco', eco.compiler
    

    Should solve issue #12

    opened by mapmarkus 2
  • Security issue

    Security issue

    Eco's default __escape implementation doesn't escape single quotes, which makes XSS attacks like the following possible:

    <input type='text' value='<%= @value %>'>
    

    with a @value of x'onmouseover='alert(document.domain), an XSS occurs.

    This fork fixes the issue by using Underscore's escape.

    opened by cantino 0
  • How to output an object in eco

    How to output an object in eco

    Hi, I'm new to CoffeeScript and Eco. I'm trying to understand how to output a javascript object to the screen for debugging purposes. I tried something like this:

    <%= document.write {'dog','cat'} %>
    

    But it does not seem to recognize the document piece. I'm sure I'm missing something big here, but I can't seem to find any documentation about eco to set me on the write course, and this seems to work in pure CoffeeScript. Any help would be much appreciated.

    opened by ezmiller 0
  • Updates code samples in readme that require colon suffix

    Updates code samples in readme that require colon suffix

    This is just a simple documentation update for the readme to mention the colon requirement when capturing. This syntax is a little different than normal CoffeeScript, so it's important to mention it.

    opened by andrewhavens 0
  • Document requirement for colon after functions

    Document requirement for colon after functions

    I am using Backbone and Eco templates in my Rails application. My template has the following code:

      <% @collection.each (model)-> %>
        <% console.log model.get('name') %>
        <p><%= model.get('name') %></p>
        <p><%= model.get('description') %></p>
      <% end %>
    

    For some reason, the HTML is blank. The name and description are not displayed. However, the console.log method outputs the correct data. What am I doing wrong?

    opened by andrewhavens 5
Owner
Sam Stephenson
Sam Stephenson
Embedded JavaScript templates -- http://ejs.co

Embedded JavaScript templates Installation $ npm install ejs Features Control flow with <% %> Escaped output with <%= %> (escape function configurable

Matthew Eernisse 6.6k Sep 16, 2022
Semi-embedded JS template engine that supports helpers, filters, partials, and template inheritance. 4KB minzipped, written in TypeScript ⛺

squirrelly Documentation - Chat - RunKit Demo - Playground Summary Squirrelly is a modern, configurable, and blazing fast template engine implemented

Squirrelly 439 Sep 3, 2022
Embedded JS template engine for Node, Deno, and the browser. Lighweight, fast, and pluggable. Written in TypeScript

eta (η) Documentation - Chat - RunKit Demo - Playground Summary Eta is a lightweight and blazing fast embedded JS templating engine that works inside

Eta 620 Sep 16, 2022
A simpler static site generator. An alternative to Jekyll. Transforms a directory of templates (of varying types) into HTML.

eleventy ?? ⚡️ A simpler static site generator. An alternative to Jekyll. Written in JavaScript. Transforms a directory of templates (of varying types

Eleventy 12.8k Sep 19, 2022
My templates for the Templater Obsidian.md plugin.

Christian's Templater Templates Found a template your like? Make sure you copy the raw file - not what Github renders. Click this button to see the ra

Christian Bager Bach Houmann 132 Sep 18, 2022
A set a periodic note templates for Obsidian.md.

MK's Periodic Note Templates A set of periodic note templates for Obsidian.md. Before You Start... Please note that these templates generally suit my

null 103 Sep 16, 2022
Browser In The Browser (BITB) Templates

BITB Browser templates for Browser In The Browser (BITB) attack. More information: https://mrd0x.com/browser-in-the-browser-phishing-attack/ Usage Eac

mrd0x 2.4k Sep 19, 2022
Embedded CoffeeScript templates

Eco: Embedded CoffeeScript templates Eco lets you embed CoffeeScript logic in your markup. It's like EJS and ERB, but with CoffeeScript inside the <%

Sam Stephenson 1.7k Sep 8, 2022
Embedded JavaScript templates -- http://ejs.co

Embedded JavaScript templates Installation $ npm install ejs Features Control flow with <% %> Escaped output with <%= %> (escape function configurable

Matthew Eernisse 6.6k Sep 16, 2022
Embedded JavaScript templates for node

EJS Embedded JavaScript templates. NOTE: Version 2 of EJS makes some breaking changes with this version (notably, removal of the filters feature). Wor

TJ Holowaychuk 4.4k Sep 19, 2022
A Node.js CMS written in CoffeeScript, with a user friendly backend

Nodizecms A Node.js CMS written in CoffeeScript, with a user friendly backend Status NodizeCMS is still under heavy development, there's a ton of unim

Nodize CMS 175 Jul 15, 2022
Semi-embedded JS template engine that supports helpers, filters, partials, and template inheritance. 4KB minzipped, written in TypeScript ⛺

squirrelly Documentation - Chat - RunKit Demo - Playground Summary Squirrelly is a modern, configurable, and blazing fast template engine implemented

Squirrelly 439 Sep 3, 2022
Embedded JS template engine for Node, Deno, and the browser. Lighweight, fast, and pluggable. Written in TypeScript

eta (η) Documentation - Chat - RunKit Demo - Playground Summary Eta is a lightweight and blazing fast embedded JS templating engine that works inside

Eta 620 Sep 16, 2022
:seedling: Next-Gen AI-Assisted Isomorphic Application Engine for Embedded, Console, Mobile, Server and Desktop

lychee.js Mono Repository Important Notes to follow through Installation Quirks: The lycheejs-engine Repository needs to be installed to the path /opt

Cookie Engineer 787 Sep 20, 2022
Open Source and Embedded Nano Faucet

NanoDrop Open Source, Transparent and Embedded Nano Faucet Visit: https://nanodrop.io This project was created to help bring Nano to the masses. Fauce

Anarkrypto 29 Sep 15, 2022
Example VS Code plugin that uses embedded Omega Edit bindings to generate content

Ωedit Edit for VS Code Example VS Code plugin that uses embedded Omega Edit bindings to generate content. Build Requirements Bindings compiled against

Concurrent Technologies Corporation (CTC) 2 Jan 14, 2022
🚀🚀 A Shopify embedded app starter template, with updated dependencies, session storage, app context and examples for basic functionalities.

Shopify Node App Starter This is a starter template for embedded shopify apps based on the shopify cli node app. Contributions to create the perfect s

Carsten Lebek 106 Sep 14, 2022
A lightweight extension to automatically detect and provide verbose warnings for embedded iframe elements in order to protect against Browser-In-The-Browser (BITB) attacks.

Enhanced iFrame Protection - Browser Extension Enhanced iFrame Protection (EIP) is a lightweight extension to automatically detect and provide verbose

odacavo 4 Sep 15, 2022