Added support extends, block, and include. #58

wants to merge 6 commits into


None yet

10 participants


Layouts is supported using extends and blocks. To reduce repetition, blocks can have defaults. See for details.

Include is also implemented to support mixins.

Finally, in order to support blocks with defaults, I ended rewriting the parser... hope that's ok...

oldcookie added some commits Jul 19, 2012
@oldcookie oldcookie Added extends, blocks, and include. Changed parsing to use XRegExp.
Added extends, blocks, and include. See readme for usage.

As part of the change for blocks, needed to have better parsing
capabilities so blocks can have default and be replaced properly.  As a
result, most of the parsing code was changed.
@oldcookie oldcookie Revert "Added extends, blocks, and include. Changed parsing to use XR…

This reverts commit 6fced8d.
@oldcookie oldcookie Revert "Revert "Added extends, blocks, and include. Changed parsing t…
…o use XRegExp.""

This reverts commit 65459c3.
@oldcookie oldcookie Corrected some formatting
Messed up on some formatting with previous checkin.
@oldcookie oldcookie Added a note that <% end %> are required for blocks 0a97d23
@oldcookie oldcookie Move reading/tokenizing of template into tokenize
Didn't like how I implemented the loading of layout, moved it down to
tokenize, so that top level tokenize returns a completely tokenized set
of input.

Couple of things:

  • Blocks - Right now blocks need an end statement to demarcate the end of the block. So every time a block is defined or referenced, one need to put in the following, even if the block was empty.

    <% block foo %>
    <% end %>

While I am ok with that, I can see some people getting a bit annoyed with this. One alternative I thought about, was to use a do end pair like:

<% block someblock do %>
<% end %>

so that block only starts if do is specified. block without do would be treated as empty blocks.

  • Currently, only blocks that appears after an extend are treated as an argument. Not sure if that's right or wrong, since most of the time extend is probably going to be the first statement in a file. Let me know what you think.
@tj tj commented on the diff Jul 19, 2012
@@ -61,7 +61,9 @@ require.register("ejs.js", function(module, exports, require){
var utils = require('./utils')
- , fs = require('fs');
+ , fs = require('fs')
+ , path = require('path')
+ , XRegExp = require('xregexp').XRegExp;
tj Jul 19, 2012 Owner

what's this?

oldcookie Jul 19, 2012

I can probably do without path, it's just a util for concatenating file paths. Xregexp provide some enhanced support for reyes that missing in some js implementations.

@tj tj commented on the diff Jul 19, 2012
@@ -149,84 +151,248 @@ function rethrow(err, str, filename, lineno){
- * Parse the given `str` of ejs, returning the function body.
+ * Token, used to represent a token/node during parsing
+ * and contains parsing info related to each token/node.
+ */
+Token = function (type, text, lineno, meta, children){
tj Jul 19, 2012 Owner

missing var ?

oldcookie Jul 19, 2012

Yeah will add when I get home.

@tj tj commented on the diff Jul 19, 2012
+ * Convert the provided string into a list of tokens/nodes.
+ *
+ * @param {String} str The string to tokenize.
+ * @param {Token[]} blocks Blocks defined in the current scope.
+ * @return {Token[]} List of resulting tokens.
+ */
+Parser.prototype.tokenize = function (str, blocks){
+ var tokens = []
+ , extNodes = []
+ , myBlocks = []
+ , lineno = 1
+ , stack = []
+ , layout = false
+ , ejsRE = XRegExp.cache('^(?<op>[=-](?<filter>:)?)?(?<body>.*?)\\s*(?<slurp>-)?$', 'mn')
tj Jul 19, 2012 Owner

the escape stuff is fine but we really don't need named capture groups, it would be nicer just to use what everyone in js-land is familiar with

oldcookie Jul 19, 2012

Not really from the js world myself, so you would prefer back reference by index instead?

tj commented Jul 19, 2012

i like the intent here but it might take me a while to review since you rewrote a pretty large portion of it

Sorry about that, tried to stick to the original, but supporting default blocks with the original parser was a bit tough. So I ended up making more chagea than I anticipated.

tj commented Jul 19, 2012

yeah understandable, we should have created an AST like Jade but the scope of EJS kinda changed with these features. im implementing includes right now the way that I would do them at least then we can add blocks in after

Well, there's is sort of an AST in what I've put in, creating a proper AST using that code wouldn't require a lot of changes.

aziz commented Aug 27, 2012

Can't wait to see this gets merged

chovy commented Sep 21, 2012

I also am waiting for blocks and extend -- will be nice for scripts and stylesheets.

jeromegn commented Oct 1, 2012

+1 Been using QEJS in the meantime...

drefined commented Nov 2, 2012



dgbeck commented Apr 12, 2013

what is the hold up I wonder? such a useful feature, love the ejs syntax but lack of blocks is a big down side. thx for all the great work on this project


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment