Permalink
Browse files

View engines system

  • Loading branch information...
1 parent cc4d7c6 commit 6dc741fcbe25b73cd5b9431ce811d6e449e34145 @eldargab committed May 15, 2012
View
@@ -0,0 +1,19 @@
+var basename = require('path').basename
+var extname = require('path').extname
+
+var engines = module.exports = function (ext) {
+ var factory = engines.extensions[ext] || engines[ext]
+ return factory()
+}
+
+require('fs').readdirSync(__dirname).forEach(function (item) {
+ if (~['index.js', '.', '..'].indexOf(item)) return
+ item = item.replace(/\.\w+$/, '')
+ engines[item] = require('./' + item)
+})
+
+engines.extensions = {
+ '.jade': engines.jade,
+ '.md': engines.markdown,
+ '.less': engines.less
+}
View
@@ -0,0 +1,9 @@
+module.exports = function () {
+ var renderFile = require('jade').renderFile
+ return {
+ renderFile: function (file, cb) {
+ renderFile(file, this, cb)
+ },
+ contentType: 'text/html'
+ }
+}
View
@@ -0,0 +1,31 @@
+var fs = require('fs')
+var Parser
+
+module.exports = function () {
+ Parser = require('less').Parser
+ return {
+ contentType: 'text/css',
+ renderFile: render
+ }
+}
+
+function render (file, cb) {
+ var parser = new Parser({
+ filename: file,
+ paths: this.root ? [this.root] : null
+ })
+
+ fs.readFile(file, 'utf8', function (error, string) {
+ if (error) return cb(error)
+ parser.parse(string, function (err, tree) {
+ if (err) return cb(err)
+ try {
+ var out = tree.toCSS()
+ }
+ catch (e) {
+ return cb(e)
+ }
+ cb(null, out)
+ })
+ })
+}
View
@@ -0,0 +1,15 @@
+module.exports = function () {
+ var Markdown = require('markdown').markdown
+
+ return {
+ contentType: 'text/html',
+
+ render: function (string, cb) {
+ var dialect = this.dialect
+ if (typeof dialect == 'string')
+ dialect = Markdown.dialects[dialect]
+ var out = Markdown.toHTML(string, dialect)
+ cb(null, out)
+ }
+ }
+}
View
@@ -1,9 +0,0 @@
-var STATUS_CODES = require('http').STATUS_CODES
-
-module.exports = function (code, text) {
- var msg = STATUS_CODES[code]
- if (text) msg = msg + '. ' + text
- var err = new Error(msg)
- err.status = code
- return err
-}
View
@@ -1,7 +1,37 @@
var Middleware = require('./middleware')
-var PathHandler = require('./path-handler')
+var Lookup = require('./path-lookup')
+var Render = require('./render')
+var engines = require('./engines')
+var mix = require('./util').mix
module.exports = function (opts) {
opts = opts || {}
- return Middleware(opts.root, PathHandler(opts))
-}
+ opts.root = opts.root || process.cwd()
+ opts.pathHandler = opts.pathHandler || Render(function (ext) {
+ return opts.engine
+ ? opts.engine(ext)
+ : engine(ext, opts)
+ })
+ return Middleware(opts.root, Lookup(function (path, req, res, next) {
+ opts.pathHandler(path, req, res, next)
+ }))
+}
+
+function engine (ext, opts) {
+ var cach = engine.cach
+ var eng = cach[ext]
+ if (eng === undefined) {
+ eng = cach[ext] = opts[ext] || engines(ext) || null
+ if (eng) {
+ var extName = ext.substring(1)
+ mix(eng, {root: opts.root}, opts['*'], opts[extName])
+ }
+ }
+ return eng ? Object.create(eng) : null
+}
+
+engine.cach = {}
+
+module.exports.engine = engine
+module.exports.engines = engines
+module.exports.Render = Render
View
@@ -1,6 +1,6 @@
var parseUrl = require('url').parse
var PATH = require('path')
-var error = require('./http-error')
+var error = require('./util').httpError
module.exports = function (root, handle) {
if (typeof root == 'function') {
@@ -10,6 +10,8 @@ module.exports = function (root, handle) {
root = root || process.cwd()
return function fileHandler (req, res, next) {
+ if (req.method != 'GET' && req.method != 'HEAD') return next()
+
var path = decodeUri(parseUrl(req.url).pathname)
if (path instanceof URIError) return next(error(400))
@@ -1,27 +1,10 @@
var fs = require('fs')
var PATH = require('path')
-module.exports = function (opts) {
+module.exports = function (onfile, opts) {
opts = opts || {}
-
- var fsStat = opts.fsStat = opts.fsStat || fs.stat
- var readdir = opts.fsDir = opts.fsDir || fs.readdir
- var exts = opts.exts || {}
-
- function Options (req, res, next) {
- this.req = req
- this.res = res
- this.next = next
- }
-
- Options.prototype = opts
-
- function handleFile (file, req, res, next) {
- var ext = PATH.extname(file)
- var handler = exts[ext] || exts['*']
- if (!handler) return next()
- handler(file, new Options(req, res, next))
- }
+ var fsStat = opts.fsStat || fs.stat
+ var readdir = opts.fsDir || fs.readdir
function lookup (path, cb) {
var dir = PATH.dirname(path)
@@ -45,12 +28,12 @@ module.exports = function (opts) {
if (error) return next()
if (files.length == 0) return next()
if (files.length > 1) return next() // TODO: Respond with 300?
- handleFile(files[0], req, res, next)
+ onfile(files[0], req, res, next)
})
} else if (stat.isDirectory()) {
handle(PATH.join(path, 'index'), req, res, next)
} else {
- handleFile(path, req, res, next)
+ onfile(path, req, res, next)
}
})
}
View
@@ -0,0 +1,27 @@
+var onerror = require('./util').nextOnENOENT
+var extname = require('path').extname
+var fs = require('fs')
+
+module.exports = function (Engine) {
+ return function render (file, req, res, next) {
+ var engine = Engine(extname(file))
+ if (engine && engine.renderFile) {
+ engine.renderFile(file, function (error, out) {
+ if (error) return onerror(error, next)
+ res.set('Content-Type', engine.contentType)
+ res.send(out)
+ })
+ } else if (engine && engine.render) {
+ fs.readFile(file, 'utf8', function (error, string) {
+ if (error) return onerror(error, next)
+ engine.render(string, function (error, out) {
+ if (error) return next(error)
+ res.set('Content-Type', engine.contentType)
+ res.send(out)
+ })
+ })
+ } else {
+ res.sendfile(file)
+ }
+ }
+}
View
@@ -0,0 +1,23 @@
+var STATUS_CODES = require('http').STATUS_CODES
+
+exports.httpError = function (code, text) {
+ var msg = STATUS_CODES[code]
+ if (text) msg = msg + '. ' + text
+ var err = new Error(msg)
+ err.status = code
+ return err
+}
+
+exports.nextOnENOENT = function (error, next) {
+ error.code == 'ENOENT' ? next() : next(error)
+}
+
+exports.mix = function (t, var_src) {
+ for (var i = 1; i < arguments.length; i++) {
+ var src = arguments[i]
+ for (var key in src) {
+ t[key] = src[key]
+ }
+ }
+ return t
+}
View
@@ -4,5 +4,9 @@
"description": "Serve your jades, markdowns, lesses like static files",
"version": "0.0.0",
"main": "lib/index",
- "devDependencies": {}
+ "devDependencies": {
+ "less": "*",
+ "jade": "*",
+ "markdown": "*"
+ }
}
@@ -0,0 +1 @@
+h1 #{hello} #{world}
@@ -0,0 +1 @@
+@import "main";
@@ -0,0 +1,5 @@
+@color: #4D926F;
+
+#header {
+ color: @color;
+}
@@ -0,0 +1 @@
+#Hello
View
@@ -0,0 +1,37 @@
+var express = require('express')
+var Request = require('./support/http').Request
+var Views = require('../lib')
+
+describe('Integration tests', function () {
+ beforeEach(function () {
+ this.app = express()
+ this.req = new Request(this.app)
+ })
+
+ function testEngine (name, options, fn) {
+ if (typeof options == 'function') {
+ fn = options
+ options = {}
+ }
+ options.root = __dirname + '/fixtures/' + name.toLowerCase()
+ it(name, function (done) {
+ this.app.use(Views(options))
+ fn(this.req, done)
+ })
+ }
+
+ testEngine('Less', function (req, done) {
+ req.get('/').expect('text/css', /header/, done)
+ })
+
+ testEngine('Markdown', function (req, done) {
+ req.get('/hello').expect('text/html', /<h1>Hello<\/h1>/, done)
+ })
+
+ testEngine('Jade', {
+ '*': {hello: 'hello'},
+ 'jade': {world: 'world'}
+ }, function (req, done) {
+ req.get('/hello').expect('text/html', /hello world/, done)
+ })
+})
View
@@ -9,7 +9,7 @@ describe('Middleware', function () {
handler = sinon.spy()
m = Middleware('root', handler)
next = Next()
- req = {}
+ req = { method: 'GET'}
res = {}
})
@@ -18,7 +18,7 @@ describe('Middleware', function () {
m(req, res, next)
}
- it('Should pass path, req, res and next to handler', function () {
+ it('Should pass path, req, res and next to the handler', function () {
test('/article')
handler.calledWithExactly('root/article', req, res, next).should.be.true
})
Oops, something went wrong.

0 comments on commit 6dc741f

Please sign in to comment.