index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. const assign = require('object-assign')
  2. const arrify = require('arrify')
  3. const micromatch = require('micromatch')
  4. const path = require('path')
  5. const readPkgUp = require('read-pkg-up')
  6. const requireMainFilename = require('require-main-filename')
  7. function TestExclude (opts) {
  8. assign(this, {
  9. cwd: process.cwd(),
  10. include: false,
  11. relativePath: true,
  12. configKey: null, // the key to load config from in package.json.
  13. configPath: null, // optionally override requireMainFilename.
  14. configFound: false
  15. }, opts)
  16. if (typeof this.include === 'string') this.include = [this.include]
  17. if (typeof this.exclude === 'string') this.exclude = [this.exclude]
  18. if (!this.include && !this.exclude && this.configKey) {
  19. assign(this, this.pkgConf(this.configKey, this.configPath))
  20. }
  21. if (!this.exclude || !Array.isArray(this.exclude)) {
  22. this.exclude = exportFunc.defaultExclude
  23. }
  24. if (this.include && this.include.length > 0) {
  25. this.include = prepGlobPatterns(arrify(this.include))
  26. } else {
  27. this.include = false
  28. }
  29. if (this.exclude.indexOf('**/node_modules/**') === -1) {
  30. this.exclude.push('**/node_modules/**')
  31. }
  32. this.exclude = prepGlobPatterns(
  33. [].concat(arrify(this.exclude))
  34. )
  35. this.handleNegation()
  36. }
  37. // handle the special case of negative globs
  38. // (!**foo/bar); we create a new this.excludeNegated set
  39. // of rules, which is applied after excludes and we
  40. // move excluded include rules into this.excludes.
  41. TestExclude.prototype.handleNegation = function () {
  42. if (Array.isArray(this.include)) {
  43. const includeNegated = this.include.filter(function (e) {
  44. return e.charAt(0) === '!'
  45. }).map(function (e) {
  46. return e.slice(1)
  47. })
  48. this.exclude.push.apply(this.exclude, prepGlobPatterns(includeNegated))
  49. this.include = this.include.filter(function (e) {
  50. return e.charAt(0) !== '!'
  51. })
  52. }
  53. this.excludeNegated = this.exclude.filter(function (e) {
  54. return e.charAt(0) === '!'
  55. }).map(function (e) {
  56. return e.slice(1)
  57. })
  58. this.exclude = this.exclude.filter(function (e) {
  59. return e.charAt(0) !== '!'
  60. })
  61. this.excludeNegated = prepGlobPatterns(this.excludeNegated)
  62. }
  63. TestExclude.prototype.shouldInstrument = function (filename, relFile) {
  64. var pathToCheck = filename
  65. if (this.relativePath) {
  66. relFile = relFile || path.relative(this.cwd, filename)
  67. // Don't instrument files that are outside of the current working directory.
  68. if (/^\.\./.test(path.relative(this.cwd, filename))) return false
  69. pathToCheck = relFile.replace(/^\.[\\/]/, '') // remove leading './' or '.\'.
  70. }
  71. return (
  72. !this.include ||
  73. micromatch.any(pathToCheck, this.include, {dotfiles: true})) &&
  74. (!micromatch.any(pathToCheck, this.exclude, {dotfiles: true}) ||
  75. micromatch.any(pathToCheck, this.excludeNegated, {dotfiles: true}))
  76. }
  77. TestExclude.prototype.pkgConf = function (key, path) {
  78. const obj = readPkgUp.sync({
  79. cwd: path || requireMainFilename(require)
  80. })
  81. if (obj.pkg && obj.pkg[key] && typeof obj.pkg[key] === 'object') {
  82. this.configFound = true
  83. return obj.pkg[key]
  84. } else {
  85. return {}
  86. }
  87. }
  88. function prepGlobPatterns (patterns) {
  89. return patterns.reduce(function (result, pattern) {
  90. // Allow gitignore style of directory exclusion
  91. if (!/\/\*\*$/.test(pattern)) {
  92. result = result.concat(pattern.replace(/\/$/, '') + '/**')
  93. }
  94. // Any rules of the form **/foo.js, should also match foo.js.
  95. if (/^\*\*\//.test(pattern)) {
  96. result = result.concat(pattern.replace(/^\*\*\//, ''))
  97. }
  98. return result.concat(pattern)
  99. }, [])
  100. }
  101. var exportFunc = function (opts) {
  102. return new TestExclude(opts)
  103. }
  104. exportFunc.defaultExclude = [
  105. 'coverage/**',
  106. 'packages/*/test/**',
  107. 'test/**',
  108. 'test{,-*}.js',
  109. '**/*{.,-}test.js',
  110. '**/__tests__/**',
  111. '**/node_modules/**'
  112. ]
  113. module.exports = exportFunc