| 'use strict'; |
| const {promisify} = require('util'); |
| const fs = require('fs'); |
| const path = require('path'); |
| const fastGlob = require('fast-glob'); |
| const gitIgnore = require('ignore'); |
| const slash = require('slash'); |
| |
| const DEFAULT_IGNORE = [ |
| '**/node_modules/**', |
| '**/flow-typed/**', |
| '**/coverage/**', |
| '**/.git' |
| ]; |
| |
| const readFileP = promisify(fs.readFile); |
| |
| const mapGitIgnorePatternTo = base => ignore => { |
| if (ignore.startsWith('!')) { |
| return '!' + path.posix.join(base, ignore.slice(1)); |
| } |
| |
| return path.posix.join(base, ignore); |
| }; |
| |
| const parseGitIgnore = (content, options) => { |
| const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); |
| |
| return content |
| .split(/\r?\n/) |
| .filter(Boolean) |
| .filter(line => !line.startsWith('#')) |
| .map(mapGitIgnorePatternTo(base)); |
| }; |
| |
| const reduceIgnore = files => { |
| const ignores = gitIgnore(); |
| for (const file of files) { |
| ignores.add(parseGitIgnore(file.content, { |
| cwd: file.cwd, |
| fileName: file.filePath |
| })); |
| } |
| |
| return ignores; |
| }; |
| |
| const ensureAbsolutePathForCwd = (cwd, p) => { |
| cwd = slash(cwd); |
| if (path.isAbsolute(p)) { |
| if (slash(p).startsWith(cwd)) { |
| return p; |
| } |
| |
| throw new Error(`Path ${p} is not in cwd ${cwd}`); |
| } |
| |
| return path.join(cwd, p); |
| }; |
| |
| const getIsIgnoredPredecate = (ignores, cwd) => { |
| return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p)))); |
| }; |
| |
| const getFile = async (file, cwd) => { |
| const filePath = path.join(cwd, file); |
| const content = await readFileP(filePath, 'utf8'); |
| |
| return { |
| cwd, |
| filePath, |
| content |
| }; |
| }; |
| |
| const getFileSync = (file, cwd) => { |
| const filePath = path.join(cwd, file); |
| const content = fs.readFileSync(filePath, 'utf8'); |
| |
| return { |
| cwd, |
| filePath, |
| content |
| }; |
| }; |
| |
| const normalizeOptions = ({ |
| ignore = [], |
| cwd = slash(process.cwd()) |
| } = {}) => { |
| return {ignore, cwd}; |
| }; |
| |
| module.exports = async options => { |
| options = normalizeOptions(options); |
| |
| const paths = await fastGlob('**/.gitignore', { |
| ignore: DEFAULT_IGNORE.concat(options.ignore), |
| cwd: options.cwd |
| }); |
| |
| const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); |
| const ignores = reduceIgnore(files); |
| |
| return getIsIgnoredPredecate(ignores, options.cwd); |
| }; |
| |
| module.exports.sync = options => { |
| options = normalizeOptions(options); |
| |
| const paths = fastGlob.sync('**/.gitignore', { |
| ignore: DEFAULT_IGNORE.concat(options.ignore), |
| cwd: options.cwd |
| }); |
| |
| const files = paths.map(file => getFileSync(file, options.cwd)); |
| const ignores = reduceIgnore(files); |
| |
| return getIsIgnoredPredecate(ignores, options.cwd); |
| }; |