autoIstanbulHandlers.js 5.73 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
/*
 Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */
const path = require('path')
const fs = require('fs')
const core = require(path.resolve(__dirname, '.', 'autoIstanbulCore'))
const istanbul = require('ry-istanbul-web')
const bodyParser = require('body-parser')
const ASSETS_DIR = istanbul.assetsDir
const existsSync = fs.existsSync || path.existsSync
const url = require('url')
const express = require('express')
const JS_RE = /\.js$/
const getPitchingPiles = core.getPitchingPile()
/**
 * Set default max limit to 100mb for incoming JSON and urlencoded
 * @type {String}
 */
var fileSizeMaximum = '1024mb'
var isExtended = true

function createHandler(opts, isAll) {
  /*jslint nomen: true */
  opts = opts || {}
  var app = express()
  // 设置跨域
  app.all('*', (req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*')
    res.header('Access-Control-Allow-Headers', 'Content-Type')
    next()
  })
  // using separete options objects to maintain readability as the objects are getting more complex
  var urlOptions = { extended: isExtended, limit: fileSizeMaximum }
  var jsonOptions = { limit: fileSizeMaximum }

  //send static file for /asset/asset-name
  app.use('/asset', express.static(ASSETS_DIR))
  app.use('/asset', express.static(path.join(ASSETS_DIR, 'vendor')))

  app.use(bodyParser.urlencoded(urlOptions))
  app.use(bodyParser.json(jsonOptions))
  app.use(express.static(path.resolve(__dirname, '..', 'istanbulGather')));

  //show main page for coverage report for /
  app.get('/', function (req, res) {
    var origUrl = url.parse(req.originalUrl).pathname,
      origLength = origUrl.length
    if (origUrl.charAt(origLength - 1) !== '/') {
      origUrl += '/'
    }
    res.type('css')
    core.render(null, res, origUrl)
  })

  //show page for specific file/ dir for /show?file=/path/to/file
  app.get('/show', function (req, res) {
    var origUrl = url.parse(req.originalUrl).pathname,
      u = url.parse(req.url).pathname,
      pos = origUrl.indexOf(u),
      file = req.query.p
    if (pos >= 0) {
      origUrl = origUrl.substring(0, pos)
    }
    if (!file) {
      res.setHeader('Content-type', 'text/plain')
      return res.end('[p] parameter must be specified')
    }
    res.type('css')
    core.render(file, res, origUrl)
  })

  //reset coverage to baseline on POST /reset
  app.post('/reset', function (req, res) {
    core.restoreBaseline()
    res.json({ ok: true })
  })

  //opt-in to allow resets on GET as well (useful for easy browser-based demos :)
  if (opts.resetOnGet) {
    app.get('/reset', function (req, res) {
      core.restoreBaseline()
      res.json({ ok: true })
    })
  }

  //return global coverage object on /object as JSON
  app.get('/object', function (req, res) {
    getPitchingPiles ? res.json(core.getCoverageObject() || {}) : {}
  })

  //merge client coverage posted from browser
  app.post('/client', function (req, res) {
    var body = req.body
    // let parse = JSON.parse(req.body.coverage)
    if (!(body && typeof body === 'object') && !getPitchingPiles) {
      //probably needs to be more robust
      return res.send(400, 'Please post an object with content-type: application/json and The global variable IS PITCHING PILE DATA IS not true')
    }
    if (!(body && typeof body === 'object')) {
      //probably needs to be more robust
      return res.send(400, 'Please post an object with content-type: application/json')
    }
    if (!getPitchingPiles) {
      return res.send(400, 'The global variable IS PITCHING PILE DATA IS not true')
    }
    core.mergeClientCoverage(body)
    res.json({
      ok: true
    })
  })

  return app
}

function defaultClientMatcher(req) {
  var parsed = url.parse(req.url)
  return parsed.pathname && parsed.pathname.match(JS_RE)
}

function defaultPathTransformer(root) {
  return function (req) {
    var parsed = url.parse(req.url),
      pathName = parsed.pathname
    if (pathName && pathName.charAt(0) === '/') {
      pathName = pathName.substring(1)
    }
    return path.resolve(root, pathName)
  }
}

function clientHandler(matcher, pathTransformer, opts) {
  var verbose = opts.verbose

  return function (req, res, next) {
    if (!matcher(req)) {
      return next()
    }
    var fullPath = pathTransformer(req)
    if (!fullPath) {
      return next()
    }

    if (!core.getInstrumenter()) {
      console.error(
        'No instrumenter set up, please call createHandler() before you use the client middleware'
      )
      return next()
    }
    if (!existsSync(fullPath)) {
      console.warn('Could not find file [' + fullPath + '], ignoring')
      return next()
    }
    fs.readFile(fullPath, 'utf8', function (err, contents) {
      var instrumented
      if (err) {
        console.warn('Error reading file: ' + fullPath)
        return next()
      }
      try {
        instrumented = core.getInstrumenter().instrumentSync(contents, fullPath)
        if (verbose) {
          console.log('Sending instrumented code for: ' + fullPath + ', url:' + req.url)
        }
        res.setHeader('Content-type', 'application/javascript')
        return res.send(instrumented)
      } catch (ex) {
        console.warn('Error instrumenting file:' + fullPath)
        return next()
      }
    })
  }
}

function createClientHandler(root, opts) {
  opts = opts || {}

  var app = express(),
    matcher = opts.matcher || defaultClientMatcher,
    pathTransformer = opts.pathTransformer || defaultPathTransformer(root)
  app.get('*', clientHandler(matcher, pathTransformer, opts))
  return app
}

module.exports = {
  createClientHandler: createClientHandler,
  createHandler: createHandler,
  hookLoader: core.hookLoader,
  globalVariable: core.globalVariable,
  getPitchingPile: core.getPitchingPile
}