1 module.exports = promzard
2 promzard.PromZard = PromZard
6 var util = require('util')
8 var crypto = require('crypto')
9 var EventEmitter = require('events').EventEmitter
10 var read = require('read')
12 var Module = require('module').Module
13 var path = require('path')
15 function promzard (file, ctx, cb) {
16 if (typeof ctx === 'function') cb = ctx, ctx = null;
18 var pz = new PromZard(file, ctx)
20 pz.on('data', function (data) {
24 promzard.fromBuffer = function (buf, ctx, cb) {
27 filename = '\0' + Math.random();
28 } while (files[filename])
30 var ret = promzard(filename, ctx, cb)
31 delete files[filename]
35 function PromZard (file, ctx) {
36 if (!(this instanceof PromZard))
37 return new PromZard(file, ctx)
38 EventEmitter.call(this)
41 this.unique = crypto.randomBytes(8).toString('hex')
45 PromZard.prototype = Object.create(
46 EventEmitter.prototype,
52 enumerable: false } } )
54 PromZard.prototype.load = function () {
58 fs.readFile(this.file, 'utf8', function (er, d) {
59 if (er && this.backupFile) {
60 this.file = this.backupFile
61 delete this.backupFile
65 return this.emit('error', this.error = er)
71 PromZard.prototype.loaded = function () {
72 this.ctx.prompt = this.makePrompt()
73 this.ctx.__filename = this.file
74 this.ctx.__dirname = path.dirname(this.file)
75 this.ctx.__basename = path.basename(this.file)
76 var mod = this.ctx.module = this.makeModule()
77 this.ctx.require = function (path) {
78 return mod.require(path)
80 this.ctx.require.resolve = function(path) {
81 return Module._resolveFilename(path, mod);
83 this.ctx.exports = mod.exports
85 this.script = this.wrap(files[this.file])
86 var fn = vm.runInThisContext(this.script, this.file)
87 var args = Object.keys(this.ctx).map(function (k) {
90 try { var res = fn.apply(this.ctx, args) }
91 catch (er) { this.emit('error', er) }
93 typeof res === 'object' &&
94 exports === mod.exports &&
95 Object.keys(exports).length === 1) {
98 this.result = mod.exports
103 PromZard.prototype.makeModule = function () {
104 var mod = new Module(this.file, module)
106 mod.filename = this.file
108 mod.paths = Module._nodeModulePaths(path.dirname(this.file))
112 PromZard.prototype.wrap = function (body) {
113 var s = '(function( %s ) { %s\n })'
114 var args = Object.keys(this.ctx).join(', ')
115 return util.format(s, args, body)
118 PromZard.prototype.makePrompt = function () {
120 return prompt.bind(this)
123 for (var i = 0; i < arguments.length; i++) {
125 if (typeof a === 'string' && p)
127 else if (typeof a === 'string')
129 else if (typeof a === 'function')
131 else if (a && typeof a === 'object') {
138 try { return this.unique + '-' + this.prompts.length }
139 finally { this.prompts.push([p, d, t]) }
143 PromZard.prototype.walk = function (o, cb) {
145 cb = cb || function (er, res) {
147 return this.emit('error', this.error = er)
149 return this.emit('data', res)
152 var keys = Object.keys(o)
154 var len = keys.length
165 if (v && typeof v === 'object') {
166 return this.walk(v, function (er, res) {
167 if (er) return cb(er)
172 typeof v === 'string' &&
173 v.indexOf(this.unique) === 0) {
174 var n = +v.substr(this.unique.length + 1)
175 var prompt = this.prompts[n]
176 if (isNaN(n) || !prompt)
179 // default to the key
180 if (undefined === prompt[0])
183 // default to the ctx value, if there is one
184 if (undefined === prompt[1])
185 prompt[1] = this.ctx[k]
187 return this.prompt(prompt, function (er, res) {
190 return this.emit('error', this.error = er);
192 console.log(er.message)
199 } else if (typeof v === 'function') {
200 try { return v.call(this.ctx, function (er, res) {
202 return this.emit('error', this.error = er)
204 // back up so that we process this one again.
205 // this is because it might return a prompt() call in the cb.
209 catch (er) { this.emit('error', er) }
212 // made it to the end of the loop, maybe
218 PromZard.prototype.prompt = function (pdt, cb) {
224 cb = function (cb) { return function (er, data) {
227 if (!er && res instanceof Error && !!res.notValid) {
232 catch (er) { this.emit('error', er) }
236 read({ prompt: prompt + ':' , default: def }, cb)