2017-05-28 00:38:50 +02:00
|
|
|
const assign = require('object-assign')
|
|
|
|
const arrify = require('arrify')
|
|
|
|
const micromatch = require('micromatch')
|
|
|
|
const path = require('path')
|
|
|
|
const readPkgUp = require('read-pkg-up')
|
|
|
|
const requireMainFilename = require('require-main-filename')
|
|
|
|
|
|
|
|
function TestExclude (opts) {
|
|
|
|
assign(this, {
|
|
|
|
cwd: process.cwd(),
|
|
|
|
include: false,
|
|
|
|
relativePath: true,
|
|
|
|
configKey: null, // the key to load config from in package.json.
|
|
|
|
configPath: null, // optionally override requireMainFilename.
|
|
|
|
configFound: false
|
|
|
|
}, opts)
|
|
|
|
|
|
|
|
if (typeof this.include === 'string') this.include = [this.include]
|
|
|
|
if (typeof this.exclude === 'string') this.exclude = [this.exclude]
|
|
|
|
|
|
|
|
if (!this.include && !this.exclude && this.configKey) {
|
|
|
|
assign(this, this.pkgConf(this.configKey, this.configPath))
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.exclude || !Array.isArray(this.exclude)) {
|
|
|
|
this.exclude = exportFunc.defaultExclude
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.include && this.include.length > 0) {
|
|
|
|
this.include = prepGlobPatterns(arrify(this.include))
|
|
|
|
} else {
|
|
|
|
this.include = false
|
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
if (this.exclude.indexOf('**/node_modules/**') === -1) {
|
2017-05-28 00:38:50 +02:00
|
|
|
this.exclude.push('**/node_modules/**')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.exclude = prepGlobPatterns(
|
|
|
|
[].concat(arrify(this.exclude))
|
|
|
|
)
|
2017-08-14 05:01:11 +02:00
|
|
|
|
|
|
|
this.handleNegation()
|
2017-05-28 00:38:50 +02:00
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
// handle the special case of negative globs
|
|
|
|
// (!**foo/bar); we create a new this.excludeNegated set
|
|
|
|
// of rules, which is applied after excludes and we
|
|
|
|
// move excluded include rules into this.excludes.
|
|
|
|
TestExclude.prototype.handleNegation = function () {
|
|
|
|
if (Array.isArray(this.include)) {
|
|
|
|
const includeNegated = this.include.filter(function (e) {
|
|
|
|
return e.charAt(0) === '!'
|
|
|
|
}).map(function (e) {
|
|
|
|
return e.slice(1)
|
|
|
|
})
|
|
|
|
this.exclude.push.apply(this.exclude, prepGlobPatterns(includeNegated))
|
|
|
|
this.include = this.include.filter(function (e) {
|
|
|
|
return e.charAt(0) !== '!'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
this.excludeNegated = this.exclude.filter(function (e) {
|
|
|
|
return e.charAt(0) === '!'
|
|
|
|
}).map(function (e) {
|
|
|
|
return e.slice(1)
|
|
|
|
})
|
2017-05-28 00:38:50 +02:00
|
|
|
this.exclude = this.exclude.filter(function (e) {
|
2017-08-14 05:01:11 +02:00
|
|
|
return e.charAt(0) !== '!'
|
2017-05-28 00:38:50 +02:00
|
|
|
})
|
2017-08-14 05:01:11 +02:00
|
|
|
this.excludeNegated = prepGlobPatterns(this.excludeNegated)
|
2017-05-28 00:38:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TestExclude.prototype.shouldInstrument = function (filename, relFile) {
|
|
|
|
var pathToCheck = filename
|
|
|
|
|
|
|
|
if (this.relativePath) {
|
|
|
|
relFile = relFile || path.relative(this.cwd, filename)
|
|
|
|
|
|
|
|
// Don't instrument files that are outside of the current working directory.
|
|
|
|
if (/^\.\./.test(path.relative(this.cwd, filename))) return false
|
|
|
|
|
|
|
|
pathToCheck = relFile.replace(/^\.[\\/]/, '') // remove leading './' or '.\'.
|
|
|
|
}
|
|
|
|
|
2017-08-14 05:01:11 +02:00
|
|
|
return (
|
|
|
|
!this.include ||
|
|
|
|
micromatch.any(pathToCheck, this.include, {dotfiles: true})) &&
|
|
|
|
(!micromatch.any(pathToCheck, this.exclude, {dotfiles: true}) ||
|
|
|
|
micromatch.any(pathToCheck, this.excludeNegated, {dotfiles: true}))
|
2017-05-28 00:38:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TestExclude.prototype.pkgConf = function (key, path) {
|
|
|
|
const obj = readPkgUp.sync({
|
|
|
|
cwd: path || requireMainFilename(require)
|
|
|
|
})
|
|
|
|
|
|
|
|
if (obj.pkg && obj.pkg[key] && typeof obj.pkg[key] === 'object') {
|
|
|
|
this.configFound = true
|
|
|
|
return obj.pkg[key]
|
|
|
|
} else {
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function prepGlobPatterns (patterns) {
|
|
|
|
return patterns.reduce(function (result, pattern) {
|
|
|
|
// Allow gitignore style of directory exclusion
|
|
|
|
if (!/\/\*\*$/.test(pattern)) {
|
|
|
|
result = result.concat(pattern.replace(/\/$/, '') + '/**')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any rules of the form **/foo.js, should also match foo.js.
|
|
|
|
if (/^\*\*\//.test(pattern)) {
|
|
|
|
result = result.concat(pattern.replace(/^\*\*\//, ''))
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.concat(pattern)
|
|
|
|
}, [])
|
|
|
|
}
|
|
|
|
|
|
|
|
var exportFunc = function (opts) {
|
|
|
|
return new TestExclude(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
exportFunc.defaultExclude = [
|
|
|
|
'coverage/**',
|
2018-09-20 02:56:13 +02:00
|
|
|
'packages/*/test/**',
|
2017-05-28 00:38:50 +02:00
|
|
|
'test/**',
|
|
|
|
'test{,-*}.js',
|
2018-09-20 02:56:13 +02:00
|
|
|
'**/*{.,-}test.js',
|
2017-05-28 00:38:50 +02:00
|
|
|
'**/__tests__/**',
|
|
|
|
'**/node_modules/**'
|
|
|
|
]
|
|
|
|
|
|
|
|
module.exports = exportFunc
|