laggingreflex
laggingreflex

Reputation: 34687

How to serve rendered Jade pages as if they were static HTML pages in Node Express?

Usually you render a Jade page in a route like this:

app.get('/page', function(req, res, next){
    res.render('page.jade');
});

But I want to serve all Jade pages (automatically rendered), just like how one would serve static HTML

app.use(express.static('public'))

Is there a way to do something similar for Jade?

Upvotes: 3

Views: 3581

Answers (3)

user3392439
user3392439

Reputation: 1043

Connect-jade-static is good, but not the perfect solution for me.

To begin with, here are the reasons why I needed jade:

My app is a single page app, there are no HTMLs generated from templates at runtime. Yet, I am using jade to generate HTML files because:

  1. Mixins: lots of repeated / similar code in my HTML is shortened by the use of mixins
  2. Dropdowns: I know, lots of people use ng-repeat to fill the options in a select box. This is a waste of CPU when the list is static, e.g., list of countries. The right thing to do is have the select options filled in within the HTML or partial. But then, a long list of options makes the HTML / jade hard to read. Also, very likely, the list of countries is already available elsewhere, and it doesn’t make sense to duplicate this list.

So, I decided to generate most of my HTML partials using jade at build time. But, this became a pain during development, because of the need to re-build HTMLs when the jade file changes. Yes, I could have used connect-jade-static, but I really don’t want to generate the HTMLs at run time — they are indeed static files.

So, this is what I did:

  • Added a 'use' before the usual use of express.static
  • Within this, I check for the timestamps of jade and the corresponding html file
  • If the jade file is newer, regenerate the html file
  • Call next() after the regeneration, or immediately, if regeneration is not required.
  • next() will fall-through to express.static, where the generated HTML will be served
  • Wrap the ‘use’ around a “if !production” condition, and in the build scripts, generate all the HTML files required.

This way, I can also use all the goodies express.static (like custom headers) provides and still use jade to generate these.

Some code snippets:

var express = require('express');
var fs = require('fs')
var jade = require('jade');
var urlutil = require('url');
var pathutil = require('path');

var countries = require('./countries.js');

var staticDir    = 'static';            // really static files like .css and .js
var staticGenDir = 'static.gen';        // generated static files, like .html
var staticSrcDir = 'static.src';        // source for generated static files, .jade

if (process.argv[2] != 'prod') {
    app.use(‘/static', function(req, res, next) {
        var u = urlutil.parse(req.url);
        if (pathutil.extname(u.pathname) == '.html') {
            var basename = u.pathname.split('.')[0];
            var htmlFile = staticGenDir + basename + '.html';
            var jadeFile = staticSrcDir + basename + '.jade';

            var hstat = fs.existsSync(htmlFile) ? fs.statSync(htmlFile) : null;
            var jstat = fs.existsSync(jadeFile) ? fs.statSync(jadeFile) : null;

            if ( jstat && (!hstat || (jstat.mtime.getTime() > hstat.mtime.getTime())) ) {
                var out = jade.renderFile(jadeFile, {pretty: true, countries: countries});
                fs.writeFile(htmlFile, out, function() {
                    next();
                });
            } else {
                next();
            }
        } else {
            next();
        }
    });
}

app.use('/static', express.static(staticDir));      // serve files from really static if exists
app.use('/static', express.static(staticGenDir));   // if not, look in generated static dir

In reality, I have a js file containing not just countries, but various other lists shared between node, javascript and jade.

Hope this helps someone looking for an alternative.

Upvotes: 2

laggingreflex
laggingreflex

Reputation: 34687

https://github.com/runk/connect-jade-static

Usage

Assume the following structure of your project:

/views
  /partials
    /file.jade

Let's make jade files from /views/partials web accessable:

var jadeStatic = require('connect-jade-static');

app = express();
app.configure(function() {
  app.use(jadeStatic({
    baseDir: path.join(__dirname, '/views/partials'),
    baseUrl: '/partials',
    jade: { pretty: true }
  }));
});

Now, if you start your web server and request /views/partials/file.html in browser you should be able see the compiled jade template.

Upvotes: 2

Peter Lyons
Peter Lyons

Reputation: 146174

"static" means sending existing files unchanged directly from disk to the browser. Jade can be served this way but that is pretty unusual. Usually you want to render jade to HTML on the server which by definition is not "static", it's dynamic. You do it like this:

app.get('/home', function (req, res) {
    res.render('home'); // will render home.jade and send the HTML
});

If you want to serve the jade itself for rendering in the browser, just reference it directly in the url when loading it into the browser like:

$.get('/index.jade', function (jade) {
    //...
});

Upvotes: 2

Related Questions