wallet-core/node_modules/ava/lib/ava-files.js
2017-05-28 00:40:43 +02:00

283 lines
7.6 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const Promise = require('bluebird');
const slash = require('slash');
const globby = require('globby');
const flatten = require('lodash.flatten');
const autoBind = require('auto-bind');
const defaultIgnore = require('ignore-by-default').directories();
const multimatch = require('multimatch');
function handlePaths(files, excludePatterns, globOptions) {
// Convert Promise to Bluebird
files = Promise.resolve(globby(files.concat(excludePatterns), globOptions));
const searchedParents = new Set();
const foundFiles = new Set();
function alreadySearchingParent(dir) {
if (searchedParents.has(dir)) {
return true;
}
const parentDir = path.dirname(dir);
if (parentDir === dir) {
// We have reached the root path
return false;
}
return alreadySearchingParent(parentDir);
}
return files
.map(file => {
file = path.resolve(globOptions.cwd, file);
if (fs.statSync(file).isDirectory()) {
if (alreadySearchingParent(file)) {
return null;
}
searchedParents.add(file);
let pattern = path.join(file, '**', '*.js');
if (process.platform === 'win32') {
// Always use `/` in patterns, harmonizing matching across platforms
pattern = slash(pattern);
}
return handlePaths([pattern], excludePatterns, globOptions);
}
// `globby` returns slashes even on Windows. Normalize here so the file
// paths are consistently platform-accurate as tests are run.
return path.normalize(file);
})
.then(flatten)
.filter(file => file && path.extname(file) === '.js')
.filter(file => {
if (path.basename(file)[0] === '_' && globOptions.includeUnderscoredFiles !== true) {
return false;
}
return true;
})
.map(file => path.resolve(file))
.filter(file => {
const alreadyFound = foundFiles.has(file);
foundFiles.add(file);
return !alreadyFound;
});
}
const defaultExcludePatterns = () => [
'!**/node_modules/**',
'!**/fixtures/**',
'!**/helpers/**'
];
const defaultIncludePatterns = () => [
'test.js',
'test-*.js',
'test',
'**/__tests__',
'**/*.test.js'
];
const defaultHelperPatterns = () => [
'**/__tests__/helpers/**/*.js',
'**/__tests__/**/_*.js',
'**/test/helpers/**/*.js',
'**/test/**/_*.js'
];
const getDefaultIgnorePatterns = () => defaultIgnore.map(dir => `${dir}/**/*`);
// Used on paths before they're passed to multimatch to harmonize matching
// across platforms
const matchable = process.platform === 'win32' ? slash : (path => path);
class AvaFiles {
constructor(options) {
options = options || {};
let files = (options.files || []).map(file => {
// `./` should be removed from the beginning of patterns because
// otherwise they won't match change events from Chokidar
if (file.slice(0, 2) === './') {
return file.slice(2);
}
return file;
});
if (files.length === 0) {
files = defaultIncludePatterns();
}
this.excludePatterns = defaultExcludePatterns();
this.files = files;
this.sources = options.sources || [];
this.cwd = options.cwd || process.cwd();
autoBind(this);
}
findTestFiles() {
return handlePaths(this.files, this.excludePatterns, {
cwd: this.cwd,
cache: Object.create(null),
statCache: Object.create(null),
realpathCache: Object.create(null),
symlinks: Object.create(null)
});
}
findTestHelpers() {
return handlePaths(defaultHelperPatterns(), ['!**/node_modules/**'], {
cwd: this.cwd,
includeUnderscoredFiles: true,
cache: Object.create(null),
statCache: Object.create(null),
realpathCache: Object.create(null),
symlinks: Object.create(null)
});
}
isSource(filePath) {
let mixedPatterns = [];
const defaultIgnorePatterns = getDefaultIgnorePatterns();
const overrideDefaultIgnorePatterns = [];
let hasPositivePattern = false;
this.sources.forEach(pattern => {
mixedPatterns.push(pattern);
// TODO: Why not just `pattern[0] !== '!'`?
if (!hasPositivePattern && pattern[0] !== '!') {
hasPositivePattern = true;
}
// Extract patterns that start with an ignored directory. These need to be
// rematched separately.
if (defaultIgnore.indexOf(pattern.split('/')[0]) >= 0) {
overrideDefaultIgnorePatterns.push(pattern);
}
});
// Same defaults as used for Chokidar
if (!hasPositivePattern) {
mixedPatterns = ['package.json', '**/*.js'].concat(mixedPatterns);
}
filePath = matchable(filePath);
// Ignore paths outside the current working directory.
// They can't be matched to a pattern.
if (/^\.\.\//.test(filePath)) {
return false;
}
const isSource = multimatch(filePath, mixedPatterns).length === 1;
if (!isSource) {
return false;
}
const isIgnored = multimatch(filePath, defaultIgnorePatterns).length === 1;
if (!isIgnored) {
return true;
}
const isErroneouslyIgnored = multimatch(filePath, overrideDefaultIgnorePatterns).length === 1;
if (isErroneouslyIgnored) {
return true;
}
return false;
}
isTest(filePath) {
const excludePatterns = this.excludePatterns;
const initialPatterns = this.files.concat(excludePatterns);
// Like in `api.js`, tests must be `.js` files and not start with `_`
if (path.extname(filePath) !== '.js' || path.basename(filePath)[0] === '_') {
return false;
}
// Check if the entire path matches a pattern
if (multimatch(matchable(filePath), initialPatterns).length === 1) {
return true;
}
// Check if the path contains any directory components
const dirname = path.dirname(filePath);
if (dirname === '.') {
return false;
}
// Compute all possible subpaths. Note that the dirname is assumed to be
// relative to the working directory, without a leading `./`.
const subpaths = dirname.split(/[\\/]/).reduce((subpaths, component) => {
const parent = subpaths[subpaths.length - 1];
if (parent) {
// Always use `/`` to makes multimatch consistent across platforms
subpaths.push(`${parent}/${component}`);
} else {
subpaths.push(component);
}
return subpaths;
}, []);
// Check if any of the possible subpaths match a pattern. If so, generate a
// new pattern with **/*.js.
const recursivePatterns = subpaths
.filter(subpath => multimatch(subpath, initialPatterns).length === 1)
// Always use `/` to makes multimatch consistent across platforms
.map(subpath => `${subpath}/**/*.js`);
// See if the entire path matches any of the subpaths patterns, taking the
// excludePatterns into account. This mimicks the behavior in api.js
return multimatch(matchable(filePath), recursivePatterns.concat(excludePatterns)).length === 1;
}
getChokidarPatterns() {
let paths = [];
let ignored = [];
this.sources.forEach(pattern => {
if (pattern[0] === '!') {
ignored.push(pattern.slice(1));
} else {
paths.push(pattern);
}
});
// Allow source patterns to override the default ignore patterns. Chokidar
// ignores paths that match the list of ignored patterns. It uses anymatch
// under the hood, which supports negation patterns. For any source pattern
// that starts with an ignored directory, ensure the corresponding negation
// pattern is added to the ignored paths.
const overrideDefaultIgnorePatterns = paths
.filter(pattern => defaultIgnore.indexOf(pattern.split('/')[0]) >= 0)
.map(pattern => `!${pattern}`);
ignored = getDefaultIgnorePatterns().concat(ignored, overrideDefaultIgnorePatterns);
if (paths.length === 0) {
paths = ['package.json', '**/*.js'];
}
paths = paths.concat(this.files);
return {
paths,
ignored
};
}
}
module.exports = AvaFiles;
module.exports.defaultIncludePatterns = defaultIncludePatterns;
module.exports.defaultExcludePatterns = defaultExcludePatterns;