UNPKG

bare-cov

Version:

Generate coverage reports using Bare and Node.js

113 lines (89 loc) 3.96 kB
const { isWindows } = require('which-runtime') const path = require('path') const process = require('process') const picomatch = require('picomatch') const DEFAULT_EXCLUDES = [ 'coverage/**', 'packages/*/test{,s}/**', '**/*.d.ts', 'test{,s}/**', 'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}', '**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}', '**/__tests__/**', '**/{ava,babel,nyc}.config.{js,cjs,mjs}', '**/jest.config.{js,cjs,mjs,ts}', '**/{karma,rollup,webpack}.config.js', '**/.{eslint,mocha}rc.{js,cjs}' ] const DEFAULT_EXTENSIONS = ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx'] function isOutsideDir(dir, filename) { return isWindows ? !path.resolve(dir, filename).startsWith(path.resolve(dir) + path.sep) : /^\.\./.test(path.relative(dir, filename)) } // Based off https://github.com/istanbuljs/test-exclude/blob/master/index.js with glob-related code removed and minimatch replaced with picomatch class TestExclude { constructor(opts = {}) { this.cwd = opts.cwd ?? process.cwd() this.include = opts.include ?? [] this.exclude = opts.exclude ?? DEFAULT_EXCLUDES this.extension = opts.extension ?? DEFAULT_EXTENSIONS this.excludeNodeModules = opts.excludeNodeModules || true this.relativePath = opts.relativePath || true if (typeof this.include === 'string') this.include = [this.include] if (typeof this.exclude === 'string') this.exclude = [this.exclude] if (typeof this.extension === 'string') this.extension = [this.extension] else if (this.extension.length === 0) this.extension = false if (this.include && this.include.length > 0) this.include = prepGlobPatterns([].concat(this.include)) else this.include = false if (this.excludeNodeModules && !this.exclude.includes('**/node_modules/**')) this.exclude = this.exclude.concat('**/node_modules/**') this.exclude = prepGlobPatterns([].concat(this.exclude)) this.handleNegation() } /* 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. */ handleNegation() { const noNeg = (e) => e.charAt(0) !== '!' const onlyNeg = (e) => e.charAt(0) === '!' const stripNeg = (e) => e.slice(1) if (Array.isArray(this.include)) { const includeNegated = this.include.filter(onlyNeg).map(stripNeg) this.exclude.push(...prepGlobPatterns(includeNegated)) this.include = this.include.filter(noNeg) } this.excludeNegated = this.exclude.filter(onlyNeg).map(stripNeg) this.exclude = this.exclude.filter(noNeg) this.excludeNegated = prepGlobPatterns(this.excludeNegated) } shouldInstrument(filename, relFile) { if (this.extension && !this.extension.some((ext) => filename.endsWith(ext))) return false let 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 (isOutsideDir(this.cwd, filename)) return false pathToCheck = relFile.replace(/^\.[\\/]/, '') // remove leading './' or '.\'. } const dot = { dot: true, windows: isWindows } const matches = (pattern) => picomatch.isMatch(pathToCheck, pattern, dot) return ( (!this.include || this.include.some(matches)) && (!this.exclude.some(matches) || this.excludeNegated.some(matches)) ) } } function prepGlobPatterns(patterns) { return patterns.reduce((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) }, []) } module.exports = TestExclude