Comparing version 0.0.2 to 0.0.3
@@ -6,4 +6,3 @@ #!/usr/bin/env node | ||
, util = require('util') | ||
, Loader = require('../lib/loader') | ||
, VM = require('../lib/VM'); | ||
, AJS = require('../lib/ajs'); | ||
@@ -23,22 +22,22 @@ // node-cli clobbers process.argv. argh. | ||
, base = path.join(filename); | ||
Loader.load(filename, opts, function(err, compiled) { | ||
AJS._load(filename, opts, function(err, template) { | ||
if(err) return console.error(err.stack); | ||
if(opts.tree) | ||
return util.print(util.inspect(compiled, false, 100) + "\n"); | ||
return util.print(util.inspect(template, false, 100) + "\n"); | ||
else if(opts.source) | ||
return util.print(compiled + "\n"); | ||
return util.print(template + "\n"); | ||
try { | ||
new VM(compiled, {filename: filename}) | ||
.on('data', function(data) { | ||
util.print(data); | ||
}).on('end', function() { | ||
console.log(); | ||
}).render(); | ||
} catch (err) { | ||
err.message = "In " + filename + ", " + err.message; | ||
throw err; | ||
} | ||
template() | ||
.on('data', function(data) { | ||
util.print(data); | ||
}) | ||
.on('error', function(err) { | ||
console.error(); | ||
console.error(err.stack); | ||
}) | ||
.on('end', function() { | ||
console.log(); | ||
}); | ||
}); | ||
}); |
@@ -1,2 +0,2 @@ | ||
// AJS 0.0.1 | ||
// AJS 0.0.3 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
@@ -20,5 +20,2 @@ // AJS may be freely distributed under the MIT license. | ||
// [loader.js](loader.html) | ||
// ------------- | ||
// [compiler.js](compiler.html) | ||
@@ -25,0 +22,0 @@ // ------------- |
220
lib/ajs.js
@@ -1,17 +0,12 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// [« Back to Index](index.html) | ||
var path = require('path') | ||
, Loader = require('./loader') | ||
, VM = require('./vm'); | ||
var fs = require('fs') | ||
, path = require('path') | ||
, Compiler = require('./compiler'); | ||
// If you need low-level access to an AJS template, simply require it, | ||
// bind to its `data`, `error` and `end` events, and call `.render(<context>)`. | ||
// If you need lower-level access to an AJS template, simply require it, call it | ||
// with a locals object `template(<locals>)`, and bind to its `data`, | ||
// `error` and `end` events. | ||
require.extensions['.ajs'] = function(module, filename) { | ||
module.exports = new VM(Loader.loadSync(filename), {filename: filename}); | ||
module.exports = Loader.loadSync(filename); | ||
return module; | ||
@@ -24,3 +19,3 @@ }; | ||
// The main AJS export is a Connect middleware function. By adding `ajs()` to your stack, | ||
// any middleware down the line will have a `res.render('/path', <context>)` | ||
// any middleware down the line will have a `res.render('/path', <locals>)` | ||
// function that accepts a template path and context object. | ||
@@ -33,6 +28,6 @@ var AJS = module.exports = function AJS(opts) { | ||
return function(req, res, next) { | ||
res.render = function(filename, context) { | ||
res.render = function(filename, locals, opts) { | ||
var filename = normalizeFilename(path.join(templateDir, filename)); | ||
Loader.load(filename, {}, function(err, compiled) { | ||
AJS._load(filename, opts, function(err, template) { | ||
if(err) { | ||
@@ -53,12 +48,12 @@ if(err.code == 'ENOENT' || err.code == 'EBADF') { | ||
// As data becomes available from the template, we pass it on to the client immediately. | ||
new VM(compiled, {filename: filename}) | ||
.on('data', function(data) { | ||
res.write(data); | ||
}).on('error', function(e) { | ||
logError(e); | ||
res.statusCode = 500; | ||
res.end('Internal server error'); | ||
}).on('end', function() { | ||
res.end(); | ||
}).render(context); | ||
template(locals) | ||
.on('data', function(data) { | ||
res.write(data); | ||
}).on('error', function(e) { | ||
console.error(e.stack); | ||
res.statusCode = 500; | ||
res.end('Internal server error'); | ||
}).on('end', function() { | ||
res.end(); | ||
}); | ||
}); | ||
@@ -74,7 +69,7 @@ } | ||
// If you're looking for a simpler way to build a quick templated site, | ||
// you can use the `ajs.serve('dir', <context>)` middleware and AJS will map request URLs | ||
// you can use the `ajs.serve('dir', <locals>)` middleware and AJS will map request URLs | ||
// directly to file and directory paths. Simply create a context containing | ||
// a data source and any utilities, and your entire app can live in your templates! | ||
// If this reminds of you PHP, just remember you're asyncronous now. | ||
AJS.serve = function serve(rootDir, context, opts) { | ||
AJS.serve = function serve(rootDir, locals, opts) { | ||
return function(req, res, next) { | ||
@@ -84,5 +79,3 @@ var path = normalizeFilename(req.url) | ||
Loader.load(filename, opts, function(err, compiled) { | ||
if(!err && !compiled) throw new Error(); | ||
AJS._load(filename, opts, function(err, template) { | ||
if(err) { | ||
@@ -97,14 +90,14 @@ if(err.code == 'ENOENT' || err.code == 'EBADF') { | ||
context.request = req; | ||
locals.request = req; | ||
new VM(compiled, {filename: filename}) | ||
.on('data', function(data) { | ||
res.write(data); | ||
}).on('error', function(e) { | ||
logError(e); | ||
res.statusCode = 500; | ||
res.end('Internal server error'); | ||
}).on('end', function() { | ||
res.end(); | ||
}).render(context); | ||
template(locals) | ||
.on('data', function(data) { | ||
res.write(data); | ||
}).on('error', function(e) { | ||
console.error(e.stack); | ||
res.statusCode = 500; | ||
res.end('Internal server error'); | ||
}).on('end', function() { | ||
res.end(); | ||
}); | ||
}); | ||
@@ -114,2 +107,138 @@ } | ||
// AJS.compile | ||
// ------------- | ||
// While we can't support ExpressJS yet due to its syncronous handling of | ||
// [template engines](https://github.com/visionmedia/express/blob/master/lib/view.js#L421) | ||
// and [responses](https://github.com/visionmedia/express/blob/master/lib/response.js#L115), | ||
// we can still support a similar API. | ||
AJS.compile = function(str, opts) { | ||
opts = opts || {}; | ||
var filename = opts.filename = opts.filename ? JSON.stringify(opts.filename) : 'undefined' | ||
, compiled; | ||
if(!(compiled = Cache._store[filename])) | ||
compiled = Cache._store[filename] = new Compiler(str, opts).compile(); | ||
return compiled; | ||
} | ||
// AJS.render | ||
// ------------- | ||
// Again, we can't exactly emulate the API of EJS (as its syncronous), but we can come | ||
// close with the use of a callback. Note that this is not for use in practice as its | ||
// still blocking until the rendering is complete, so nothing is being flushed to the client. | ||
AJS.render = function(str, opts, callback) { | ||
var buffer = []; | ||
template = AJS.compile(str, opts); | ||
template(opts.locals) | ||
.on('data', function(data) { | ||
buffer.push(data); | ||
}).on('error', function(err) { | ||
throw err; | ||
}).on('end', function() { | ||
callback(buffer.join('')); | ||
}); | ||
} | ||
// Return a template function compiled from the requested file. | ||
// If a cached object is found and the file hasn't been updated, return that. | ||
// Otherwise, attempt to read and compile the file asyncronously, calling back | ||
// with a compiled template function if successful or an error if not. | ||
AJS._load = function load(filename, opts, callback) { | ||
opts = opts || {}; | ||
var compiled | ||
, cache = (typeof opts.cache != 'undefined') ? opts.cache : true; | ||
Cache.get(filename, function(err, cached) { | ||
if(err) return callback(err); | ||
if(cache && cached) { | ||
callback(null, cached); | ||
} else { | ||
fs.readFile(filename, 'utf-8', function(err, source) { | ||
if(err) return callback(err); | ||
try { | ||
compiled = new Compiler(source, opts).compile(); | ||
} catch(e) { | ||
e.message = "In " + filename + ", " + e.message; | ||
return callback(e); | ||
} | ||
Cache.set(filename, compiled); | ||
callback(null, compiled); | ||
}); | ||
} | ||
}); | ||
} | ||
// The same as Loader.load, but syncronous. If an error occurs it will be thrown. | ||
AJS._loadSync = function loadSync(filename, opts) { | ||
opts = opts || {}; | ||
var compiled | ||
, cache = (typeof opts.cache != 'undefined') ? opts.cache : true; | ||
try { | ||
if (cache && (cached = Cache.getSync(filename))) { | ||
return cached; | ||
} else { | ||
opts.filename = filename; | ||
compiled = new Compiler(fs.readFileSync(filename, 'utf8'), opts).compile(); | ||
Cache.set(filename, compiled); | ||
return compiled; | ||
} | ||
} catch(e) { | ||
e.message = "In " + filename + ", " + e.message; | ||
throw e; | ||
} | ||
} | ||
// When we include templates from a running VM, we specificy the `bare` option | ||
// so the compiler doesn't wrap the template in a new VM. | ||
AJS._loadInclude = function loadInclude(filename, opts) { | ||
return AJS._loadSync(filename, {bare: true}); | ||
} | ||
// A very simple singleton memory cache to store compiled template functions for | ||
// extremely fast retrieval. We use a file's mtime (modified time) to determine | ||
// when the cache is stale and needs to be refreshed. | ||
var Cache = new (function() { | ||
this._store = {}; | ||
this.get = function(filename, callback) { | ||
var cached = this._store[filename]; | ||
if(!cached) return callback(null, null); | ||
fs.stat(filename, function(err, stat) { | ||
if(err) return callback(err); | ||
if(cached.mtime.toString() != stat.mtime.toString()) | ||
callback(null, null); | ||
else callback(null, cached.template); | ||
}); | ||
} | ||
this.getSync = function(filename) { | ||
var cached = this._store[filename]; | ||
if(!cached) return null; | ||
var stat = fs.statSync(filename); | ||
if(cached.mtime.toString() != stat.mtime.toString()) | ||
return null; | ||
else return cached.template; | ||
} | ||
this.set = function(filename, template) { | ||
var self = this; | ||
fs.stat(filename, function(err, stat) { | ||
if(stat) { | ||
self._store[filename] = { | ||
template: template | ||
, mtime: stat.mtime | ||
}; | ||
}; | ||
}); | ||
} | ||
})(); | ||
function normalizeFilename(path) { | ||
@@ -121,9 +250,2 @@ if(path.slice(-1) == '/') | ||
return path; | ||
} | ||
function logError(e) { | ||
var stack = e.stack.split('\n').slice(1); | ||
stack.unshift(' at (' + e.filename + ":" + e.line + ')'); | ||
console.error(e.toString()); | ||
console.error(stack.join('\n')); | ||
} | ||
} |
@@ -1,16 +0,8 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// | ||
// Thanks to mishoo/uglifyjs for most of this! | ||
// [« Back to Index](index.html) | ||
var util = require('util') | ||
, vm = require('vm') | ||
, g = require('./grammar') | ||
, Lexer = require('./lexer') | ||
, Parser = require('./parser') | ||
, VM = require('./vm') | ||
, Node = Parser.Node | ||
@@ -21,15 +13,17 @@ | ||
this._onlyTree = opts.tree || false; | ||
this._onlySource = opts.source || false; | ||
this._outFunc = "__ajs.out"; | ||
this._escFunc = "__ajs.esc"; | ||
this._cbFunc = "__ajs.cb"; | ||
this._endFunc = "__ajs.end"; | ||
this._lineFunc = "__ajs.ln"; | ||
this._errFunc = "__ajs.err"; | ||
this._onlyTree = (typeof opts.tree != 'undefined') ? opts.tree : false; | ||
this._onlySource = (typeof opts.source != 'undefined') ? opts.source : false; | ||
this._bareFunc = (typeof opts.bare != 'undefined') ? opts.bare : false; | ||
this._cbFunc = "__ajs.cb"; | ||
this._outFunc = "__ajs.out"; | ||
this._escFunc = "__ajs.esc"; | ||
this._lineFunc = "__ajs.ln"; | ||
this._endFunc = "__ajs.end"; | ||
this._errFunc = "__ajs.err"; | ||
this.filename = opts.filename; | ||
this.source = source; | ||
this.line = 0; | ||
this.lineCount = 0; | ||
this.line = 1; | ||
this.lineCount = 1; | ||
this.stack = []; | ||
@@ -40,4 +34,5 @@ this.compiled = ""; | ||
Compiler.prototype.compile = function() { | ||
var lexer = new Lexer(this.source, {includeComments: false}); | ||
var lexer = new Lexer(this.source, {includeComments: false}) | ||
, self = this; | ||
this.tree = new Parser(lexer).parse(); | ||
@@ -48,14 +43,19 @@ this.lineCount = lexer._line; | ||
this.compiled = "(function(__ajs, __context) {\n" + | ||
"var include = __ajs.inc\n" + | ||
" , print = __ajs.out;\n" + | ||
"with(__context) {\n" + | ||
"try {\n" + | ||
this._make(this.tree) + ";\n" + this._endFunc + "();\n" + | ||
"} catch(e) { " + this._errFunc +"(e) }\n" + | ||
"}})"; | ||
this.compiled = "var include = __ajs.inc\n" + | ||
" , print = __ajs.out;\n" + | ||
"with(__locals) {\n" + | ||
"try {\n" + | ||
this._make(this.tree) + ";\n" + this._endFunc + "();\n" + | ||
"} catch(e) { " + this._errFunc +"(e) }\n" + | ||
"}"; | ||
if(this._onlySource) return this.compiled; | ||
var fn = new Function('__ajs, __locals', this.compiled); | ||
if(this._onlySource) return fn.toString(); | ||
return vm.runInThisContext(this.compiled, this.filename); | ||
if(this._bareFunc) return fn; | ||
return function(locals) { | ||
return new VM(fn, {filename: self.filename}).render(locals); | ||
}; | ||
} | ||
@@ -80,17 +80,21 @@ | ||
proto[Node.OUTPUT] = function(output) { | ||
return this._format([this._outFunc + "(\"" + cleanOutput(output) + "\")"]); | ||
return this._format([this._outFunc + "('" + formatOutput(output).replace(/[\n]+/g, '\\n') + "')"]); | ||
}; | ||
proto[Node.EMBED] = function(statements) { | ||
return this._format([this._outFunc + "(" + this._escFunc + "(" + cleanEmbed(this._blockStatements(statements).join('')) + "))"]); | ||
} | ||
proto[Node.ESCAPED] = function(statement) { | ||
return this._format([this._escFunc + "(" + formatEmbed(this[Node.STATEMENT](statement)) + ")"]); | ||
}; | ||
proto[Node.EMBED_RAW] = function(statements) { | ||
return this._format([this._outFunc + "(" + cleanEmbed(this._blockStatements(statements).join('')) + ")"]); | ||
} | ||
proto[Node.EMBED] = function(statement) { | ||
return this._format([this._outFunc + "(" + this._escFunc + "(" + formatEmbed(this[Node.STATEMENT](statement)) + "))"]); | ||
}; | ||
proto[Node.EMBED_RAW] = function(statement) { | ||
return this._format([this._outFunc + "(" + formatEmbed(this[Node.STATEMENT](statement)) + ")"]); | ||
}; | ||
proto[Node.BLOCK] = function(statements) { | ||
if (!statements) return ";"; | ||
if (statements.length == 0) return "{}"; | ||
return "{" + this._blockStatements(statements).join('; ') + "}"; | ||
return "{" + this._blockStatements(this._optimizeOutputNodes(statements)).join('; ') + "}"; | ||
}; | ||
@@ -380,3 +384,3 @@ | ||
if(this._lineFunc && stat.line != this.line && (!stat.children[0] || !member(stat.children[0].type, [Node.NAME, Node.STRING]))) { | ||
if(this._lineFunc && stat.line && stat.line != this.line && (!stat.children[0] || !member(stat.children[0].type, [Node.NAME, Node.STRING]))) { | ||
code = this._lineFunc + "(" + stat.line + "); " + code; | ||
@@ -465,2 +469,46 @@ this.line = stat.line; | ||
Compiler.prototype._optimizeOutputNodes = function(statements) { | ||
if(statements.length < 2) return statements; | ||
var newStats = [] | ||
, binStat = new Node(Node.BINARY, '+'); | ||
statements.forEach(function(stat, i, list) { | ||
switch(stat.type) { | ||
case Node.OUTPUT: | ||
addStat(new Node(Node.STRING, formatOutput(stat.children[0]))); | ||
break; | ||
case Node.EMBED: | ||
addStat(new Node(Node.ESCAPED, stat.children[0])); | ||
break; | ||
case Node.EMBED_RAW: | ||
addStat(stat.children[0]); | ||
break; | ||
default: | ||
if(i < list.length - 1) { | ||
nextEmbed(stat); | ||
} | ||
} | ||
}); | ||
nextEmbed(); | ||
return newStats.length ? newStats : statements; | ||
function addStat(stat) { | ||
if(binStat.children.length < 3) | ||
binStat.push(stat); | ||
else binStat = new Node(Node.BINARY, '+', binStat, stat); | ||
} | ||
function nextEmbed(stat) { | ||
if(binStat.children.length == 1) { | ||
if(stat) newStats.push(stat); | ||
return; | ||
} | ||
var node = new Node(Node.EMBED_RAW, new Node(Node.STATEMENT, binStat)); | ||
if(stat) node.line = stat.line; | ||
newStats.push(node); | ||
binStat = new Node(Node.BINARY, '+'); | ||
} | ||
} | ||
Compiler.prototype._wrapCb = function(arg) { | ||
@@ -574,7 +622,7 @@ return this._cbFunc + "(" + arg + ")"; | ||
function cleanOutput(str) { | ||
return str.replace(/\"/g, '\\"').replace(/[\n]+/g, '\\n'); | ||
function formatOutput(str) { | ||
return str.replace(/\'/g, "\\'"); | ||
} | ||
function cleanEmbed(str) { | ||
function formatEmbed(str) { | ||
return str.replace(/[\n]+/g, '\\n') | ||
@@ -581,0 +629,0 @@ } |
@@ -1,7 +0,1 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// | ||
// Thanks to mishoo/uglifyjs for most of this! | ||
@@ -8,0 +2,0 @@ |
@@ -1,7 +0,1 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// | ||
// Thanks to mishoo/uglifyjs for most of this! | ||
@@ -8,0 +2,0 @@ |
@@ -1,7 +0,1 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// | ||
// Thanks to mishoo/uglifyjs for most of this! | ||
@@ -219,5 +213,5 @@ | ||
case "=": | ||
return this._node(Node.EMBED, [this._statement()]); | ||
return this._node(Node.EMBED, this._statement()); | ||
case "-": | ||
return this._node(Node.EMBED_RAW, [this._statement()]); | ||
return this._node(Node.EMBED_RAW, this._statement()); | ||
default: this._unexpected(); | ||
@@ -674,2 +668,3 @@ } | ||
, 'embed_raw' | ||
, 'escaped' | ||
, 'expression_list' | ||
@@ -676,0 +671,0 @@ , 'for' |
@@ -1,7 +0,1 @@ | ||
// AJS 0.0.1 | ||
// (c) 2011 Evan Owen, LifeKraze LLC. | ||
// AJS may be freely distributed under the MIT license. | ||
// For details and documentation: | ||
// http://github.com/kainosnoema/ajs | ||
// [« Back to Index](index.html) | ||
@@ -12,3 +6,3 @@ | ||
, EventEmitter = require('events').EventEmitter | ||
, Loader = require('./loader'); | ||
, AJS = require('./ajs'); | ||
@@ -20,13 +14,13 @@ // AJS Virtual Machine | ||
// and buffers output until callbacks return and we're ready to flush | ||
var VM = module.exports = function VM(compiled, opts) { | ||
var VM = module.exports = function VM(func, opts) { | ||
EventEmitter.call(this); | ||
this.filename = path.resolve(process.cwd(), opts.filename); | ||
this.dirname = path.dirname(this.filename); | ||
this.filename = resolveFilename(opts.filename); | ||
this.dirname = resolveDirname(opts.filename); | ||
this.line = 0; | ||
this.line = 1; | ||
this.error = null; | ||
this.running = false; | ||
this._compiled = compiled; | ||
this._function = func; | ||
this._locals = null; | ||
this._cbs = []; | ||
@@ -41,13 +35,16 @@ this._inCb = null; | ||
// We delay the actual execution of the template function by a tick | ||
// to give us time to bind to the `data`, `error` and `end` events. | ||
VM.prototype.render = function(locals) { | ||
this._locals = locals || {}; | ||
process.nextTick(this._execute.bind(this)); | ||
return this; | ||
} | ||
// We kick off the VM by calling the compiled template function, | ||
// passing it our own context (for writes and callback handling), | ||
// as well as the one passed in for the current request. | ||
VM.prototype.render = function(context) { | ||
if(this.running) return false; | ||
this.running = true; | ||
this._context = context || {}; | ||
// passing it our own vm context (for writes and callback handling), | ||
// as well as the locals passed in for the current request. | ||
VM.prototype._execute = function() { | ||
this._depth++; | ||
this._compiled.call(this, this._vmContext(), this._runContext()); | ||
this._function.call(this, this._vmContext(), this._runLocals()); | ||
} | ||
@@ -58,3 +55,3 @@ | ||
// pass it the context you provide, and execute it under this VM. | ||
VM.prototype._include = function(request, context) { | ||
VM.prototype._include = function(request, locals) { | ||
var filename = path.join(this.dirname, request + '.ajs') | ||
@@ -64,3 +61,3 @@ if(filename == this.filename) throw new Error('self include'); | ||
try { | ||
var included = Loader.loadSync(filename, {filename: filename}) | ||
var included = AJS._loadInclude(filename); | ||
} catch(e) { | ||
@@ -71,7 +68,7 @@ if(e.code == 'ENOENT' || e.code == 'EBADF') | ||
} | ||
var includeLocals = extend(this._runLocals(), locals || {}); | ||
var includeContext = extend(this._runContext(), context || {}); | ||
this._depth++; | ||
included.call(this, this._vmContext(), includeContext); | ||
included.call(this, this._vmContext(), includeLocals); | ||
} | ||
@@ -149,3 +146,2 @@ | ||
if(this.isComplete()) { | ||
this.running = false; | ||
this.emit('end'); | ||
@@ -165,5 +161,6 @@ } | ||
this._flush(); | ||
e.filename = this.filename; | ||
e.line = this.line; | ||
e.message = e.message + ' at (' + e.filename + ":" + e.line + ')'; | ||
this.error = e; | ||
this.error.filename = this.filename; | ||
this.error.line = this.line; | ||
this.emit('error', this.error); | ||
@@ -192,5 +189,5 @@ } | ||
, out: this._write.bind(this) | ||
, ln: this._line.bind(this) | ||
, end: this._end.bind(this) | ||
, err: this._error.bind(this) | ||
, ln: this._line.bind(this) | ||
, inc: this._include.bind(this) | ||
@@ -206,16 +203,29 @@ , flush: this._flush.bind(this) | ||
// adding a few helpful global properties along the way. | ||
VM.prototype._runContext = function() { | ||
if(this._runContext_) return this._runContext_; | ||
VM.prototype._runLocals = function() { | ||
if(this._runLocals_) return this._runLocals_; | ||
var runCtx = this._runContext_ = extend({}, this._context); | ||
this._runLocals_ = extend({}, this._locals); | ||
runCtx.__filename = this.filename; | ||
runCtx.__dirname = this.dirname; | ||
runCtx.setTimeout = setTimeout; | ||
runCtx.clearTimeout = clearTimeout; | ||
this._runLocals_.__filename = this.filename; | ||
this._runLocals_.__dirname = this.dirname; | ||
return runCtx; | ||
return this._runLocals_; | ||
} | ||
var filenameCache = {}; | ||
function resolveFilename(filename) { | ||
var cached; | ||
if(cached = filenameCache[filename]) | ||
return cached; | ||
else return filenameCache[filename] = path.resolve(process.cwd(), filename); | ||
} | ||
var dirnameCache = {}; | ||
function resolveDirname(filename) { | ||
var cached; | ||
if(cached = dirnameCache[filename]) | ||
return cached; | ||
else return dirnameCache[filename] = path.dirname(filename); | ||
} | ||
function escape(expr) { | ||
@@ -222,0 +232,0 @@ return String(expr) |
{ "name" : "ajs" | ||
, "description" : "Experimental asyncronous templating in Node" | ||
, "keywords" : ["ajs", "ejs", "template", "view", "asyncronous"] | ||
, "version" : "0.0.2" | ||
, "version" : "0.0.3" | ||
, "author" : "Evan Owen <kainosnoema@gmail.com>" | ||
@@ -6,0 +6,0 @@ , "main" : "index" |
# AJS | ||
AJS is an experimental asyncronous templating language for [Node](http://nodejs.org). | ||
Currently a work in progress, but Connect middleware is functional. | ||
AJS is an experimental asyncronous templating language for [Node](http://nodejs.org). Currently a work in progress, but Connect middleware is functional. | ||
AJS is currently **NOT** compatible with the [ExpressJS](http://expressjs.com) view system due to its syncronous handling of [template engines](https://github.com/visionmedia/express/blob/master/lib/view.js#L421) and [responses](https://github.com/visionmedia/express/blob/master/lib/response.js#L115). | ||
## Installation | ||
@@ -65,10 +66,9 @@ | ||
For lower-level access, simply require the template file, bind to its `data`, `error` and `end` events, and call `.render(<context>)`, passing it an optional context object: | ||
For lower-level access to an AJS template, simply require it, call it with a locals object `template(<locals>)`, and bind to its `data`, `error` and `end` events. | ||
```` javascript | ||
var template = require('views/index'); | ||
template.on('data', function(data) { | ||
template({title: 'Hello World!'}).on('data', function(data) { | ||
console.log(data); | ||
}) | ||
template.render({title: 'Hello World!'}); | ||
}); | ||
```` | ||
@@ -75,0 +75,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
92922
2517
2
22
1