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