110 lines
2.9 KiB
JavaScript
110 lines
2.9 KiB
JavaScript
|
import process from 'node:process';
|
||
|
import fs from 'node:fs';
|
||
|
import fsPromises from 'node:fs/promises';
|
||
|
import path from 'node:path';
|
||
|
import fastGlob from 'fast-glob';
|
||
|
import gitIgnore from 'ignore';
|
||
|
import slash from 'slash';
|
||
|
import {toPath} from 'unicorn-magic';
|
||
|
import {isNegativePattern} from './utilities.js';
|
||
|
|
||
|
const defaultIgnoredDirectories = [
|
||
|
'**/node_modules',
|
||
|
'**/flow-typed',
|
||
|
'**/coverage',
|
||
|
'**/.git',
|
||
|
];
|
||
|
const ignoreFilesGlobOptions = {
|
||
|
absolute: true,
|
||
|
dot: true,
|
||
|
};
|
||
|
|
||
|
export const GITIGNORE_FILES_PATTERN = '**/.gitignore';
|
||
|
|
||
|
const applyBaseToPattern = (pattern, base) => isNegativePattern(pattern)
|
||
|
? '!' + path.posix.join(base, pattern.slice(1))
|
||
|
: path.posix.join(base, pattern);
|
||
|
|
||
|
const parseIgnoreFile = (file, cwd) => {
|
||
|
const base = slash(path.relative(cwd, path.dirname(file.filePath)));
|
||
|
|
||
|
return file.content
|
||
|
.split(/\r?\n/)
|
||
|
.filter(line => line && !line.startsWith('#'))
|
||
|
.map(pattern => applyBaseToPattern(pattern, base));
|
||
|
};
|
||
|
|
||
|
const toRelativePath = (fileOrDirectory, cwd) => {
|
||
|
cwd = slash(cwd);
|
||
|
if (path.isAbsolute(fileOrDirectory)) {
|
||
|
if (slash(fileOrDirectory).startsWith(cwd)) {
|
||
|
return path.relative(cwd, fileOrDirectory);
|
||
|
}
|
||
|
|
||
|
throw new Error(`Path ${fileOrDirectory} is not in cwd ${cwd}`);
|
||
|
}
|
||
|
|
||
|
return fileOrDirectory;
|
||
|
};
|
||
|
|
||
|
const getIsIgnoredPredicate = (files, cwd) => {
|
||
|
const patterns = files.flatMap(file => parseIgnoreFile(file, cwd));
|
||
|
const ignores = gitIgnore().add(patterns);
|
||
|
|
||
|
return fileOrDirectory => {
|
||
|
fileOrDirectory = toPath(fileOrDirectory);
|
||
|
fileOrDirectory = toRelativePath(fileOrDirectory, cwd);
|
||
|
return fileOrDirectory ? ignores.ignores(slash(fileOrDirectory)) : false;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const normalizeOptions = (options = {}) => ({
|
||
|
cwd: toPath(options.cwd) ?? process.cwd(),
|
||
|
suppressErrors: Boolean(options.suppressErrors),
|
||
|
deep: typeof options.deep === 'number' ? options.deep : Number.POSITIVE_INFINITY,
|
||
|
ignore: [...options.ignore ?? [], ...defaultIgnoredDirectories],
|
||
|
});
|
||
|
|
||
|
export const isIgnoredByIgnoreFiles = async (patterns, options) => {
|
||
|
const {cwd, suppressErrors, deep, ignore} = normalizeOptions(options);
|
||
|
|
||
|
const paths = await fastGlob(patterns, {
|
||
|
cwd,
|
||
|
suppressErrors,
|
||
|
deep,
|
||
|
ignore,
|
||
|
...ignoreFilesGlobOptions,
|
||
|
});
|
||
|
|
||
|
const files = await Promise.all(
|
||
|
paths.map(async filePath => ({
|
||
|
filePath,
|
||
|
content: await fsPromises.readFile(filePath, 'utf8'),
|
||
|
})),
|
||
|
);
|
||
|
|
||
|
return getIsIgnoredPredicate(files, cwd);
|
||
|
};
|
||
|
|
||
|
export const isIgnoredByIgnoreFilesSync = (patterns, options) => {
|
||
|
const {cwd, suppressErrors, deep, ignore} = normalizeOptions(options);
|
||
|
|
||
|
const paths = fastGlob.sync(patterns, {
|
||
|
cwd,
|
||
|
suppressErrors,
|
||
|
deep,
|
||
|
ignore,
|
||
|
...ignoreFilesGlobOptions,
|
||
|
});
|
||
|
|
||
|
const files = paths.map(filePath => ({
|
||
|
filePath,
|
||
|
content: fs.readFileSync(filePath, 'utf8'),
|
||
|
}));
|
||
|
|
||
|
return getIsIgnoredPredicate(files, cwd);
|
||
|
};
|
||
|
|
||
|
export const isGitIgnored = options => isIgnoredByIgnoreFiles(GITIGNORE_FILES_PATTERN, options);
|
||
|
export const isGitIgnoredSync = options => isIgnoredByIgnoreFilesSync(GITIGNORE_FILES_PATTERN, options);
|