| module.exports = promzard |
| promzard.PromZard = PromZard |
| |
| var fs = require('fs') |
| var vm = require('vm') |
| var util = require('util') |
| var files = {} |
| var crypto = require('crypto') |
| var EventEmitter = require('events').EventEmitter |
| var read = require('read') |
| |
| var Module = require('module').Module |
| var path = require('path') |
| |
| function promzard (file, ctx, cb) { |
| if (typeof ctx === 'function') cb = ctx, ctx = null; |
| if (!ctx) ctx = {}; |
| var pz = new PromZard(file, ctx) |
| pz.on('error', cb) |
| pz.on('data', function (data) { |
| cb(null, data) |
| }) |
| } |
| promzard.fromBuffer = function (buf, ctx, cb) { |
| var filename = 0 |
| do { |
| filename = '\0' + Math.random(); |
| } while (files[filename]) |
| files[filename] = buf |
| var ret = promzard(filename, ctx, cb) |
| delete files[filename] |
| return ret |
| } |
| |
| function PromZard (file, ctx) { |
| if (!(this instanceof PromZard)) |
| return new PromZard(file, ctx) |
| EventEmitter.call(this) |
| this.file = file |
| this.ctx = ctx |
| this.unique = crypto.randomBytes(8).toString('hex') |
| this.load() |
| } |
| |
| PromZard.prototype = Object.create( |
| EventEmitter.prototype, |
| { constructor: { |
| value: PromZard, |
| readable: true, |
| configurable: true, |
| writable: true, |
| enumerable: false } } ) |
| |
| PromZard.prototype.load = function () { |
| if (files[this.file]) |
| return this.loaded() |
| |
| fs.readFile(this.file, 'utf8', function (er, d) { |
| if (er && this.backupFile) { |
| this.file = this.backupFile |
| delete this.backupFile |
| return this.load() |
| } |
| if (er) |
| return this.emit('error', this.error = er) |
| files[this.file] = d |
| this.loaded() |
| }.bind(this)) |
| } |
| |
| PromZard.prototype.loaded = function () { |
| this.ctx.prompt = this.makePrompt() |
| this.ctx.__filename = this.file |
| this.ctx.__dirname = path.dirname(this.file) |
| this.ctx.__basename = path.basename(this.file) |
| var mod = this.ctx.module = this.makeModule() |
| this.ctx.require = function (path) { |
| return mod.require(path) |
| } |
| this.ctx.require.resolve = function(path) { |
| return Module._resolveFilename(path, mod); |
| } |
| this.ctx.exports = mod.exports |
| |
| this.script = this.wrap(files[this.file]) |
| var fn = vm.runInThisContext(this.script, this.file) |
| var args = Object.keys(this.ctx).map(function (k) { |
| return this.ctx[k] |
| }.bind(this)) |
| try { var res = fn.apply(this.ctx, args) } |
| catch (er) { this.emit('error', er) } |
| if (res && |
| typeof res === 'object' && |
| exports === mod.exports && |
| Object.keys(exports).length === 1) { |
| this.result = res |
| } else { |
| this.result = mod.exports |
| } |
| this.walk() |
| } |
| |
| PromZard.prototype.makeModule = function () { |
| var mod = new Module(this.file, module) |
| mod.loaded = true |
| mod.filename = this.file |
| mod.id = this.file |
| mod.paths = Module._nodeModulePaths(path.dirname(this.file)) |
| return mod |
| } |
| |
| PromZard.prototype.wrap = function (body) { |
| var s = '(function( %s ) { %s\n })' |
| var args = Object.keys(this.ctx).join(', ') |
| return util.format(s, args, body) |
| } |
| |
| PromZard.prototype.makePrompt = function () { |
| this.prompts = [] |
| return prompt.bind(this) |
| function prompt () { |
| var p, d, t |
| for (var i = 0; i < arguments.length; i++) { |
| var a = arguments[i] |
| if (typeof a === 'string' && p) |
| d = a |
| else if (typeof a === 'string') |
| p = a |
| else if (typeof a === 'function') |
| t = a |
| else if (a && typeof a === 'object') { |
| p = a.prompt || p |
| d = a.default || d |
| t = a.transform || t |
| } |
| } |
| |
| try { return this.unique + '-' + this.prompts.length } |
| finally { this.prompts.push([p, d, t]) } |
| } |
| } |
| |
| PromZard.prototype.walk = function (o, cb) { |
| o = o || this.result |
| cb = cb || function (er, res) { |
| if (er) |
| return this.emit('error', this.error = er) |
| this.result = res |
| return this.emit('data', res) |
| } |
| cb = cb.bind(this) |
| var keys = Object.keys(o) |
| var i = 0 |
| var len = keys.length |
| |
| L.call(this) |
| function L () { |
| if (this.error) |
| return |
| while (i < len) { |
| var k = keys[i] |
| var v = o[k] |
| i++ |
| |
| if (v && typeof v === 'object') { |
| return this.walk(v, function (er, res) { |
| if (er) return cb(er) |
| o[k] = res |
| L.call(this) |
| }.bind(this)) |
| } else if (v && |
| typeof v === 'string' && |
| v.indexOf(this.unique) === 0) { |
| var n = +v.substr(this.unique.length + 1) |
| var prompt = this.prompts[n] |
| if (isNaN(n) || !prompt) |
| continue |
| |
| // default to the key |
| if (undefined === prompt[0]) |
| prompt[0] = k |
| |
| // default to the ctx value, if there is one |
| if (undefined === prompt[1]) |
| prompt[1] = this.ctx[k] |
| |
| return this.prompt(prompt, function (er, res) { |
| if (er) { |
| if (!er.notValid) { |
| return this.emit('error', this.error = er); |
| } |
| console.log(er.message) |
| i -- |
| return L.call(this) |
| } |
| o[k] = res |
| L.call(this) |
| }.bind(this)) |
| } else if (typeof v === 'function') { |
| try { return v.call(this.ctx, function (er, res) { |
| if (er) |
| return this.emit('error', this.error = er) |
| o[k] = res |
| // back up so that we process this one again. |
| // this is because it might return a prompt() call in the cb. |
| i -- |
| L.call(this) |
| }.bind(this)) } |
| catch (er) { this.emit('error', er) } |
| } |
| } |
| // made it to the end of the loop, maybe |
| if (i >= len) |
| return cb(null, o) |
| } |
| } |
| |
| PromZard.prototype.prompt = function (pdt, cb) { |
| var prompt = pdt[0] |
| var def = pdt[1] |
| var tx = pdt[2] |
| |
| if (tx) { |
| cb = function (cb) { return function (er, data) { |
| try { |
| var res = tx(data) |
| if (!er && res instanceof Error && !!res.notValid) { |
| return cb(res, null) |
| } |
| return cb(er, res) |
| } |
| catch (er) { this.emit('error', er) } |
| }}(cb).bind(this) |
| } |
| |
| read({ prompt: prompt + ':' , default: def }, cb) |
| } |
| |