10 Sep
2012

Fixing Swig’s Template Inheritance on Express for NodeJS

Today I was playing around with the Swig templating engine on Express for NodeJS, and I came to a problem:

Extending templates doesn’t work

I was using two template files:

index.html

{% extends 'layout.html' %}

{% block site_title %}{{title}}{% endblock %}

layout.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>{% block site_title %}My App{% endblock %}</title>
</head>
<body>
    <h1>{{title}}</h1>
</body>
</html>

My app.js code was:

var express = require('express')
  , swig = require('swig')
  , cons = require('consolidate')
  , routes = require('./routes')
  , http = require('http')
  , path = require('path')
  , VIEWS_DIR = __dirname + '/views';

var app = express();

// assign the swig engine to .html files
app.engine('html', cons.swig);

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', VIEWS_DIR);
  app.set('view engine', 'html');
  app.set('view options', { layout: false });
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(require('less-middleware')({ src: __dirname + '/public' }));
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

…and I run into this error:

Error: Circular extends found on line 3 of…

But, after a few hours investigating I got it! You only have to initialize the Swig engine like this:

swig.init({
    root: VIEWS_DIR, //Note this directory is your Views directory
    allowErrors: true // allows errors to be thrown and caught by express
});

So, finally, the code looks like this:

var express = require('express')
  , swig = require('swig')
  , cons = require('consolidate')
  , routes = require('./routes')
  , http = require('http')
  , path = require('path')
  , VIEWS_DIR = __dirname + '/views';

var app = express();

swig.init({
    root: VIEWS_DIR, //Note this directory is your Views directory
    allowErrors: true // allows errors to be thrown and caught by express
});

// assign the swig engine to .html files
app.engine('html', cons.swig);

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', VIEWS_DIR);
  app.set('view engine', 'html');
  app.set('view options', { layout: false });
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(require('less-middleware')({ src: __dirname + '/public' }));
  app.use(express.static(path.join(__dirname, 'public')));
});


app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

Hope this help!
Happy coding!


Update: You can find it in the docs (https://github.com/paularmstrong/swig/blob/master/docs/express.md
https://github.com/paularmstrong/swig/blob/master/examples/express/server.js).

Thanks to @paularmstrong.