docs: function (data) {
        var include = function (path, includeData) {
          var d = utils.shallowCopy({}, data);
          if (includeData) {
            d = utils.shallowCopy(d, includeData);
          }
          var p = includeFile(path, opts)(d);
          return p;
        };
        
        var error = null;
        
        var outputStreamOpts = utils.shallowCopy({}, opts.streamOptions);
        var outputStreamBufferOpts = utils.shallowCopy({}, opts.streamOptions);
        outputStreamBufferOpts.flush = function (cb) {
          cb(error);
        }
        
        var outputStream = new PassThrough(outputStreamOpts);
        var outputStreamBuffer = new PassThrough(outputStreamBufferOpts);
        
        // outputStream.write('');
        outputStream.pipe(outputStreamBuffer);
        
        var p = utils.wrapToPromise(fn, opts.context, Promise, data || {}, escape, include, rethrow, Promise, utils.wrapToPromise, outputStream, Stream);
        
        p.shouldUseBuffer = true;
        p.noBuffer = function () {
          p.outputBuffer = ""
          p.shouldUseBuffer = false;
        }
        p.useBuffer = function () {
          p.shouldUseBuffer = true;
        }
        
        
        // do not continue the script if it is not appropriate to resume writing data to the stream.
        // https://nodejs.org/api/stream.html#stream_event_drain
        p.shouldWaitFlush = false;
        Object.defineProperty(outputStream, 'shouldWaitFlush', {
          get: function () {
            return p.shouldWaitFlush
          },
          set: function (val) {
            p.shouldWaitFlush = val
            return true;
          }
        })
        p.waitFlush = function () {
          p.shouldWaitFlush = true;
        }
        p.noWaitFlush = function () {
          p.shouldWaitFlush = false;
        }
        
        
        p.outputBuffer = ""
        // since the stream got consumed immediatly, we need another stream to buffer it;
        p.outputStream = outputStreamBuffer;
        // a test only property
        p._outputStream = outputStream;
        // since we need to escape the text, and escape only works on string
        outputStream.setEncoding('utf8');
        
        // redirect data to the buffer
        outputStream.on('data', function (data) {
          if (!p.shouldUseBuffer) return;
          p.outputBuffer += data;
        })
        
        // resolve the promise after stream end, so we can get all data.
        outputStream.on('end', function (data) {
          p.defered.resolve(p.outputBuffer)
        })
        
        // hook after generator exit normally
        p.defered.onResolve = function (defered) {
          outputStream.end();
          // force it to flush all buffer since a pipe to fulfilled target prohibit
          // the stream from flushing the data
          // outputStream.uncork();
          
          // use read directly, since uncork sometimes fail to dunp the stream
          while (outputStream.read()) {}
        }
        
        // hook for situation the generator throws
        p.defered.onReject = function (defered, err) {
          // the promise should be reject before stream got end, 
          // or it will cause the stream to think it terminate normally, 
          // since there is not any error event before the end event and promise reject
          defered.reject(err)
          
          // emit only if the user decide to listen to stream's error event instead of promise reject
          // the stream.pipe bind a error listener to stream, so we need to count it
          if (outputStreamBuffer.listenerCount('error') > 1) {
            error = err;
          }
          outputStream.end();
          // force it to flush all buffer since a pipe to fulfilled target prohibit
          // the stream from flushing the data
          // outputStream.uncork();
          
          // use read directly, since uncork sometimes fail to dunp the stream
          while (outputStream.read()) {}
        }
        
        // terminate the stream, prevent further writing
        p.defered.onInterrupt = function (cb) {
          outputStream.end();
          cb();
        }
        
        setTimeout(function() {
          p.execute();
        }, 0)
        return p;
      }