1 | 'use strict';
|
2 |
|
3 | const path = require('path');
|
4 | const { promisify } = require('util');
|
5 | const glob = promisify(require('glob'));
|
6 | const minimatch = require('minimatch');
|
7 | const { defaults } = require('@istanbuljs/schema');
|
8 | const isOutsideDir = require('./is-outside-dir');
|
9 |
|
10 | class TestExclude {
|
11 | constructor(opts = {}) {
|
12 | Object.assign(
|
13 | this,
|
14 | {relativePath: true},
|
15 | defaults.testExclude
|
16 | );
|
17 |
|
18 | for (const [name, value] of Object.entries(opts)) {
|
19 | if (value !== undefined) {
|
20 | this[name] = value;
|
21 | }
|
22 | }
|
23 |
|
24 | if (typeof this.include === 'string') {
|
25 | this.include = [this.include];
|
26 | }
|
27 |
|
28 | if (typeof this.exclude === 'string') {
|
29 | this.exclude = [this.exclude];
|
30 | }
|
31 |
|
32 | if (typeof this.extension === 'string') {
|
33 | this.extension = [this.extension];
|
34 | } else if (this.extension.length === 0) {
|
35 | this.extension = false;
|
36 | }
|
37 |
|
38 | if (this.include && this.include.length > 0) {
|
39 | this.include = prepGlobPatterns([].concat(this.include));
|
40 | } else {
|
41 | this.include = false;
|
42 | }
|
43 |
|
44 | if (
|
45 | this.excludeNodeModules &&
|
46 | !this.exclude.includes('**/node_modules/**')
|
47 | ) {
|
48 | this.exclude = this.exclude.concat('**/node_modules/**');
|
49 | }
|
50 |
|
51 | this.exclude = prepGlobPatterns([].concat(this.exclude));
|
52 |
|
53 | this.handleNegation();
|
54 | }
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 |
|
61 | handleNegation() {
|
62 | const noNeg = e => e.charAt(0) !== '!';
|
63 | const onlyNeg = e => e.charAt(0) === '!';
|
64 | const stripNeg = e => e.slice(1);
|
65 |
|
66 | if (Array.isArray(this.include)) {
|
67 | const includeNegated = this.include.filter(onlyNeg).map(stripNeg);
|
68 | this.exclude.push(...prepGlobPatterns(includeNegated));
|
69 | this.include = this.include.filter(noNeg);
|
70 | }
|
71 |
|
72 | this.excludeNegated = this.exclude.filter(onlyNeg).map(stripNeg);
|
73 | this.exclude = this.exclude.filter(noNeg);
|
74 | this.excludeNegated = prepGlobPatterns(this.excludeNegated);
|
75 | }
|
76 |
|
77 | shouldInstrument(filename, relFile) {
|
78 | if (
|
79 | this.extension &&
|
80 | !this.extension.some(ext => filename.endsWith(ext))
|
81 | ) {
|
82 | return false;
|
83 | }
|
84 |
|
85 | let pathToCheck = filename;
|
86 |
|
87 | if (this.relativePath) {
|
88 | relFile = relFile || path.relative(this.cwd, filename);
|
89 |
|
90 |
|
91 | if (isOutsideDir(this.cwd, filename)) {
|
92 | return false;
|
93 | }
|
94 |
|
95 | pathToCheck = relFile.replace(/^\.[\\/]/, '');
|
96 | }
|
97 |
|
98 | const dot = { dot: true };
|
99 | const matches = pattern => minimatch(pathToCheck, pattern, dot);
|
100 | return (
|
101 | (!this.include || this.include.some(matches)) &&
|
102 | (!this.exclude.some(matches) || this.excludeNegated.some(matches))
|
103 | );
|
104 | }
|
105 |
|
106 | globSync(cwd = this.cwd) {
|
107 | const globPatterns = getExtensionPattern(this.extension || []);
|
108 | const globOptions = { cwd, nodir: true, dot: true };
|
109 | |
110 |
|
111 | if (this.excludeNegated.length === 0) {
|
112 | globOptions.ignore = this.exclude;
|
113 | }
|
114 |
|
115 | return glob
|
116 | .sync(globPatterns, globOptions)
|
117 | .filter(file => this.shouldInstrument(path.resolve(cwd, file)));
|
118 | }
|
119 |
|
120 | async glob(cwd = this.cwd) {
|
121 | const globPatterns = getExtensionPattern(this.extension || []);
|
122 | const globOptions = { cwd, nodir: true, dot: true };
|
123 | |
124 |
|
125 | if (this.excludeNegated.length === 0) {
|
126 | globOptions.ignore = this.exclude;
|
127 | }
|
128 |
|
129 | const list = await glob(globPatterns, globOptions);
|
130 | return list.filter(file => this.shouldInstrument(path.resolve(cwd, file)));
|
131 | }
|
132 | }
|
133 |
|
134 | function prepGlobPatterns(patterns) {
|
135 | return patterns.reduce((result, pattern) => {
|
136 |
|
137 | if (!/\/\*\*$/.test(pattern)) {
|
138 | result = result.concat(pattern.replace(/\/$/, '') + '/**');
|
139 | }
|
140 |
|
141 |
|
142 | if (/^\*\*\//.test(pattern)) {
|
143 | result = result.concat(pattern.replace(/^\*\*\//, ''));
|
144 | }
|
145 |
|
146 | return result.concat(pattern);
|
147 | }, []);
|
148 | }
|
149 |
|
150 | function getExtensionPattern(extension) {
|
151 | switch (extension.length) {
|
152 | case 0:
|
153 | return '**';
|
154 | case 1:
|
155 | return `**/*${extension[0]}`;
|
156 | default:
|
157 | return `**/*{${extension.join()}}`;
|
158 | }
|
159 | }
|
160 |
|
161 | module.exports = TestExclude;
|