UNPKG

toffee

Version:

A NodeJs and browser-side templating language based on CoffeeScript with slicker tokens and syntax.

417 lines (379 loc) 15.3 kB
// Generated by CoffeeScript 1.12.7 (function() { var LockTable, MAX_CACHED_SANDBOXES, Pool, engine, fs, path, ref, sandboxCons, states, tweakables, util, utils, view, vm, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; view = require('./view').view; ref = require('./consts'), states = ref.states, tweakables = ref.tweakables; Pool = require('./pool').Pool; utils = require('./utils'); fs = require('fs'); path = require('path'); util = require('util'); vm = require('vm'); LockTable = require('iced-lock').Table; MAX_CACHED_SANDBOXES = 100; sandboxCons = function() { return vm.createContext({}); }; engine = (function() { function engine(options) { this._fn_partial = bind(this._fn_partial, this); this._fn_snippet = bind(this._fn_snippet, this); this._fn_load = bind(this._fn_load, this); this._inlineInclude = bind(this._inlineInclude, this); this.run = bind(this.run, this); this.render = bind(this.render, this); options = options || {}; this.verbose = options.verbose || false; this.pool = new Pool(sandboxCons, options.poolSize || MAX_CACHED_SANDBOXES); this.prettyPrintErrors = options.prettyPrintErrors != null ? options.prettyPrintErrors : true; this.prettyLogErrors = options.prettyLogErrors != null ? options.prettyLogErrors : true; this.autoEscape = options.autoEscape != null ? options.autoEscape : true; this.cache = options.cache != null ? options.cache : true; this.additionalErrorHandler = options.additionalErrorHandler || null; this.viewCache = {}; this.fsErrorCache = {}; this.filenameCache = {}; this.fileLockTable = new LockTable(); } engine.prototype._log = function(o) { var ref1; if (this.verbose) { if ((ref1 = typeof o) === "string" || ref1 === "number" || ref1 === "boolean") { return console.log("toffee: " + o); } else { return console.log("toffee: " + (util.inspect(o))); } } }; engine.prototype.normalizeFilename = function(dir, filename) { var cache, normalized; cache = this.filenameCache[dir]; if (cache == null) { this.filenameCache[dir] = {}; cache = {}; } normalized = cache[filename]; if (normalized == null) { normalized = path.normalize(path.resolve(dir, filename)); this.filenameCache[dir][filename] = normalized; } return normalized; }; engine.prototype.render = function(filename, options, cb) { return this.run(filename, options, cb); }; engine.prototype.run = function(filename, options, cb) { /* "options" contains the pub vars and may contain special items: layout: path to a template expecting a body var (express 2.x style, but for use with express 3.x) postProcess: a function which takes the string of output and post processes it (returning new string) __toffee.dir: path to look relative to __toffee.parent: parent file __toffee.noInheritance: if true, don't pass variables through unless explicitly passed __toffee.repress if true, don't output anything; useful with including definition files with passback of vars __toffee.autoEscape: if set as false, don't escape output of #{} vars by default */ var err, k, layout_options, post_process, ref1, ref2, ref3, ref4, ref5, res, v; if (options.prettyPrintErrors == null) { options.prettyPrintErrors = this.prettyPrintErrors; } if (options.prettyLogErrors == null) { options.prettyLogErrors = this.prettyLogErrors; } if (options.additionalErrorHandler == null) { options.additionalErrorHandler = this.additionalErrorHandler; } if (options.autoEscape == null) { options.autoEscape = this.autoEscape; } post_process = options.postProcess; options.postProcess = null; if (options != null ? options.layout : void 0) { layout_options = {}; for (k in options) { v = options[k]; if (k !== "layout") { layout_options[k] = v; } } } ref1 = this.runSync(filename, options), err = ref1[0], res = ref1[1]; if (err && this.prettyPrintErrors) { ref2 = [null, err], err = ref2[0], res = ref2[1]; } if ((!err) && (layout_options != null)) { layout_options.body = res; ref3 = this.runSync(options.layout, layout_options), err = ref3[0], res = ref3[1]; if (err && this.prettyPrintErrors) { ref4 = [null, err], err = ref4[0], res = ref4[1]; } } if ((!err) && (typeof post_process === "function")) { ref5 = this.postProcess(post_process, res), err = ref5[0], res = ref5[1]; } return cb(err, res); }; engine.prototype.postProcess = function(fn, res) { var e, err; err = null; try { res = fn(res); } catch (error) { e = error; err = e; } return [err, res]; }; engine.prototype.runSync = function(filename, options) { /* "options" the same as run() above */ var ctx, err, realpath, ref1, ref2, ref3, res, start_time, v; start_time = Date.now(); options = options || {}; options.__toffee = options.__toffee || {}; options.__toffee.dir = options.__toffee.dir || process.cwd(); realpath = this.normalizeFilename(options.__toffee.dir, filename); if (this.cache) { v = (this._viewCacheGet(realpath)) || (this._loadCacheAndMonitor(realpath, options)); } else { v = this._loadWithoutCache(realpath, options); } if (v) { if (this.fsErrorCache[realpath]) { ref1 = [new Error("Couldn't load " + realpath), null], err = ref1[0], res = ref1[1]; } else { options.__toffee.parent = realpath; options.partial = options.partial || (function(_this) { return function(fname, lvars) { return _this._fn_partial(fname, lvars, realpath, options); }; })(this); options.snippet = options.snippet || (function(_this) { return function(fname, lvars) { return _this._fn_snippet(fname, lvars, realpath, options); }; })(this); options.load = options.load || (function(_this) { return function(fname, lvars) { return _this._fn_load(fname, lvars, realpath, options); }; })(this); options.print = options.print || (function(_this) { return function(txt) { return _this._fn_print(txt, options); }; })(this); if (options.console == null) { options.console = { log: console.log }; } ctx = this.pool.get(); ref2 = v.run(options, ctx), err = ref2[0], res = ref2[1]; this.pool.release(ctx); } } else { ref3 = [new Error("Couldn't load " + realpath), null], err = ref3[0], res = ref3[1]; } this._log(realpath + " run in " + (Date.now() - start_time) + "ms"); return [err, res]; }; engine.prototype._viewCacheGet = function(filename) { if (this.viewCache[filename] == null) { return null; } else if (this.fsErrorCache[filename] == null) { return this.viewCache[filename]; } else if ((Date.now() - this.fsErrorCache[filename]) < tweakables.MISSING_FILE_RECHECK) { return this.viewCache[filename]; } else { return null; } }; engine.prototype._inlineInclude = function(filename, local_vars, parent_realpath, parent_options) { var err, i, k, len, noInheritance, options, ref1, ref2, ref3, repress, res, reserved, v; options = local_vars || {}; options.passback = {}; options.__toffee = options.__toffee || {}; options.__toffee.dir = path.dirname(parent_realpath); options.__toffee.parent = parent_realpath; noInheritance = options.__toffee.noInheritance; repress = options.__toffee.repress; reserved = {}; ref1 = ["passback", "load", "print", "partial", "snippet", "layout", "__toffee", "postProcess"]; for (i = 0, len = ref1.length; i < len; i++) { k = ref1[i]; reserved[k] = true; } if (!noInheritance) { for (k in parent_options) { v = parent_options[k]; if ((local_vars != null ? local_vars[k] : void 0) == null) { if (reserved[k] == null) { options[k] = v; } } } } ref2 = this.runSync(filename, options), err = ref2[0], res = ref2[1]; ref3 = options.passback; for (k in ref3) { v = ref3[k]; parent_options[k] = v; } return err || res; }; engine.prototype._fn_load = function(fname, lvars, realpath, options) { lvars = lvars != null ? lvars : {}; lvars.__toffee = lvars.__toffee || {}; lvars.__toffee.repress = true; return this._inlineInclude(fname, lvars, realpath, options); }; engine.prototype._fn_snippet = function(fname, lvars, realpath, options) { lvars = lvars != null ? lvars : {}; lvars.__toffee = lvars.__toffee || {}; lvars.__toffee.noInheritance = true; return this._inlineInclude(fname, lvars, realpath, options); }; engine.prototype._fn_partial = function(fname, lvars, realpath, options) { return this._inlineInclude(fname, lvars, realpath, options); }; engine.prototype._fn_print = function(txt, options) { if (options.__toffee.state === states.COFFEE) { options.__toffee.out.push(txt); return ''; } else { return txt; } }; engine.prototype._loadWithoutCache = function(filename, options) { var e, ref1, txt, v, view_options; try { txt = fs.readFileSync(filename, 'utf8'); } catch (error) { e = error; txt = "Error: Could not read " + filename; if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) { txt += " first requested in " + options.__toffee.parent; } } view_options = this._generateViewOptions(filename); v = new view(txt, view_options); return v; }; engine.prototype._loadCacheAndMonitor = function(filename, options) { var e, previous_fs_err, ref1, txt, v, view_options; previous_fs_err = this.fsErrorCache[filename] != null; try { txt = fs.readFileSync(filename, 'utf8'); if (this.fsErrorCache[filename] != null) { delete this.fsErrorCache[filename]; } } catch (error) { e = error; txt = "Error: Could not read " + filename; if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) { txt += " first requested in " + options.__toffee.parent; } this.fsErrorCache[filename] = Date.now(); } if (this.fsErrorCache[filename] && previous_fs_err && this.viewCache[filename]) { return this.viewCache[filename]; } else { view_options = this._generateViewOptions(filename); v = new view(txt, view_options); this.viewCache[filename] = v; this._monitorForChanges(filename, options); return v; } }; engine.prototype._reloadFileInBkg = function(filename, options) { this._log(filename + " acquiring lock to read"); return this.fileLockTable.acquire2({ name: filename }, (function(_this) { return function(lock) { return fs.readFile(filename, 'utf8', function(err, txt) { var ctx, ref1, v, view_options, waiting_for_view; if (!err) { _this._log((Date.now()) + " - " + filename + " changed to " + (txt != null ? txt.length : void 0) + " bytes. " + (txt != null ? typeof txt.replace === "function" ? txt.replace(/\n/g, '').slice(0, 80) : void 0 : void 0)); } waiting_for_view = false; if (err || (txt !== _this.viewCache[filename].txt)) { if (err) { _this.fsErrorCache[filename] = Date.now(); txt = "Error: Could not read " + filename; if (((ref1 = options.__toffee) != null ? ref1.parent : void 0) != null) { txt += " requested in " + options.__toffee.parent; } } if (!(err && _this.viewCache[filename].fsError)) { view_options = _this._generateViewOptions(filename); ctx = _this.pool.get(); view_options.ctx = ctx; view_options.cb = function(v) { _this._log(filename + " updated and ready"); _this.viewCache[filename] = v; _this.pool.release(ctx); _this._log(filename + " lock releasing (view_options.cb)"); return lock.release(); }; waiting_for_view = true; if (err) { view_options.fsError = true; } v = new view(txt, view_options); } } if (!waiting_for_view) { _this._log(filename + " lock releasing (not waiting for view)"); return lock.release(); } }); }; })(this)); }; engine.prototype._generateViewOptions = function(filename) { return { fileName: filename, verbose: this.verbose, prettyPrintErrors: this.prettyPrintErrors, prettyLogErrors: this.prettyLogErrors, autoEscape: this.autoEscape, additionalErrorHandler: this.additionalErrorHandler }; }; engine.prototype._monitorForChanges = function(filename, options) { /* we must continuously unwatch/rewatch because some editors/systems invoke a "rename" event and we'll end up following the wrong, old 'file' as a new one is dropped in its place. Files that are missing are ignored here because they get picked up by new calls to _loadCacheAndMonitor */ var e, fsw; if (this.fsErrorCache[filename] == null) { fsw = null; try { this._log(filename + " starting fs.watch()"); return fsw = fs.watch(filename, { persistent: true }, (function(_this) { return function(change) { _this._log(filename + " closing fs.watch()"); fsw.close(); _this._monitorForChanges(filename, options); return _this._reloadFileInBkg(filename, options); }; })(this)); } catch (error) { e = error; this._log("fs.watch() failed for " + filename + "; settings fsErrorCache = true"); return this.fsErrorCache[filename] = Date.now(); } } }; return engine; })(); exports.engine = engine; }).call(this);