Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ajs

Package Overview
Dependencies
Maintainers
0
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ajs - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

benchmark.js

31

bin/cli.js

@@ -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 @@ // -------------

@@ -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!
// [&laquo; 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
// [&laquo; 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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc