123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- 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)
- }
|