index.js 7.28 KB
Newer Older
lxyang committed
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
const path = require('path')
import { realpathSync } from 'fs'
import { execFileSync, execSync } from 'child_process'
import { declare } from '@babel/helper-plugin-utils'
// import { programVisitor } from './istanlibment'
import { programVisitor } from 'istanbul-lib-instrument'
import TestExclude from 'test-exclude'
import schema from '@istanbuljs/schema'
import parseDiffData from 'parse-diff'

function getRealpath(n) {
  try {
    return realpathSync(n) || /* istanbul ignore next */ n
  } catch (e) {
    /* istanbul ignore next */
    return n
  }
}

const memoize = new Map()
/* istanbul ignore next */
const memosep = path.sep === '/' ? ':' : ';'

function loadNycConfig (cwd, opts) {
  let memokey = cwd
  const args = [
    path.resolve(__dirname, 'load-nyc-config-sync.js'),
    cwd
  ]

  if ('nycrcPath' in opts) {
    args.push(opts.nycrcPath)

    memokey += memosep + opts.nycrcPath
  }

  /* execFileSync is expensive, avoid it if possible! */
  if (memoize.has(memokey)) {
    return memoize.get(memokey)
  }

  const result = JSON.parse(execFileSync(process.execPath, args))
  const error = result['load-nyc-config-sync-error']
  if (error) {
    throw new Error(error)
  }

  const config = {
    ...schema.defaults.babelPluginIstanbul,
    cwd,
    ...result
  }
  memoize.set(memokey, config)
  return config
}

function findConfig (opts) {
  const cwd = getRealpath(opts.cwd || process.env.NYC_CWD || /* istanbul ignore next */ process.cwd())
  const keys = Object.keys(opts)
  const ignored = Object.keys(opts).filter(s => s === 'nycrcPath' || s === 'cwd')
  if (keys.length > ignored.length) {
    // explicitly configuring options in babel
    // takes precedence.
    return {
      ...schema.defaults.babelPluginIstanbul,
      cwd,
      ...opts
    }
  }

  if (ignored.length === 0 && process.env.NYC_CONFIG) {
    // defaults were already applied by nyc
    return JSON.parse(process.env.NYC_CONFIG)
  }

  return loadNycConfig(cwd, opts)
}

function makeShouldSkip () {
  let exclude

  return function shouldSkip (file, nycConfig) {
    if (!exclude || (exclude.cwd !== nycConfig.cwd)) {
      exclude = new TestExclude({
        cwd: nycConfig.cwd,
        include: nycConfig.include,
        exclude: nycConfig.exclude,
        extension: nycConfig.extension,
        // Make sure this is true unless explicitly set to `false`. `undefined` is still `true`.
        excludeNodeModules: nycConfig.excludeNodeModules !== false
      })
    }

    return !exclude.shouldInstrument(file)
  }
}
export default declare(api => {
  api.assertVersion(7)
  const shouldSkip = makeShouldSkip()
  const t = api.types
  return {
    visitor: {
      Program: {
        enter(path) {
          // 筛选后的数据增量文件
          let diffScreen = null
          this.__dv__ = null
          this.nycConfig = findConfig(this.opts)
          const realPath = getRealpath(this.file.opts.filename)
          const { instrmenttation } = this.nycConfig // npm引入参数
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
          let IS_PITCHING_PILE_DATA = false
          let INCREMENT_DATA = false
          let BRANCH_DATA = 'origin/master'
          let processData = {}
          Object.keys(process.env).forEach(item => {
            let isItem =
              typeof process.env[item] === 'string' ? Boolean(process.env[item]) : process.env[item]
            if (item.indexOf('INCREMENT')) {
              processData.INCREMENT = isItem
            }
            if (item.indexOf('IS_PITCHING_PILE')) {
              processData.IS_PITCHING_PILE = isItem
            }
            if (item.indexOf('PROSRC')) {
              processData.PROSRC = process.env[item]
            }
            // Object.keys(processData).length
          })
          // 全局变量优先
          if (Object.keys(processData).length) {
            BRANCH_DATA = processData.BRANCH
            INCREMENT_DATA = processData.INCREMENT
            IS_PITCHING_PILE_DATA = processData.IS_PITCHING_PILE
          } else if (Object.keys(instrmenttation).length) {
            BRANCH_DATA = instrmenttation.BRANCH
            INCREMENT_DATA = instrmenttation.INCREMENT
            IS_PITCHING_PILE_DATA = instrmenttation.IS_PITCHING_PILE
          }
lxyang committed
139
          // instrumenttation (branch: git diff 跟那个分支对比, increment 是否开启增量代码检测) 拿配置到底是全量代码还是增量代码
140
          if (!IS_PITCHING_PILE_DATA) {
lxyang committed
141 142 143 144 145 146 147 148 149 150
            return
          }
          // const changeList = []
          // 下面的是零时的
          // const gitDiffCode = execSync(`git diff ${branch}`)
          // const diffData = parseDiffData(gitDiffCode.toString())
          // diffScreen = diffData.find(item => {
          //   return realPath.indexOf(item.to) > -1
          // })
          // 后面解开
151 152
          if (INCREMENT_DATA && BRANCH_DATA) {
            const gitDiffCode = execSync(`git diff ${BRANCH_DATA}`)
lxyang committed
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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
            const diffData = parseDiffData(gitDiffCode.toString())
            diffScreen = diffData.find(item => {
              return realPath.indexOf(item.to) > -1
            })
          }
          if (shouldSkip(realPath, this.nycConfig)) {
            return
          }
          let { inputSourceMap } = this.opts
          if (this.opts.useInlineSourceMaps) {
            if (!inputSourceMap && this.file.inputMap) {
              inputSourceMap = this.file.inputMap.sourcemap
            }
          }
          const visitorOptions = {}
          Object.entries(schema.defaults.instrumentVisitor).forEach(([name, defaultValue]) => {
            if (name in this.nycConfig) {
              visitorOptions[name] = this.nycConfig[name]
            } else {
              visitorOptions[name] = schema.defaults.instrumentVisitor[name]
            }
          })
          // 后增加|| !increment这个条件判断条件后面加上
            // if (diffScreen !== null && increment) {
              // if (diffScreen) {
              // if (Array.isArray(diffScreen && diffScreen.chunks)) {
              //   diffScreen.chunks.forEach(item => {
              //     let changes = Array.isArray(item.changes) ? item.changes : []
              //     changes.forEach(items => {
              //       if (items && items.add) {
              //         changeList.push(items.ln)
              //       }
              //     })
              //   })
              // }
              // changeList
              this.__dv__ = programVisitor(t, realPath, {
                ...visitorOptions,
                inputSourceMap
              })
              this.__dv__.enter(path)
            // } else {
            //   this.__dv__ = programVisitor(t, realPath, {
            //     ...visitorOptions,
            //     inputSourceMap
            //   })
            //   this.__dv__.enter(path)
            // }
        },
        exit(path) {
          if (!this.__dv__) {
            return
          }
          const result = this.__dv__.exit(path)
          if (this.opts.onCover) {
            this.opts.onCover(getRealpath(this.file.opts.filename), result.fileCoverage)
          }
        }
      }
    }
  }
})

// 增量代码babel引入方法 babel.js 或 babel.config.js
// 'plugins': [
//   ['@hliang/babel-plugin-transfrom-modules-commonjs', {
//     extension: ['.js', '.vue'],
//     instrmenttation: {
//       increment: true, // 是否全量插桩
//       branch: 'origin/master' // 对比分支
//     }
//   }]
// ]