ignore.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import process from 'node:process';
  2. import fs from 'node:fs';
  3. import path from 'node:path';
  4. import fastGlob from 'fast-glob';
  5. import gitIgnore from 'ignore';
  6. import slash from 'slash';
  7. import {toPath, isNegativePattern} from './utilities.js';
  8. const ignoreFilesGlobOptions = {
  9. ignore: [
  10. '**/node_modules',
  11. '**/flow-typed',
  12. '**/coverage',
  13. '**/.git',
  14. ],
  15. absolute: true,
  16. dot: true,
  17. };
  18. export const GITIGNORE_FILES_PATTERN = '**/.gitignore';
  19. const applyBaseToPattern = (pattern, base) => isNegativePattern(pattern)
  20. ? '!' + path.posix.join(base, pattern.slice(1))
  21. : path.posix.join(base, pattern);
  22. const parseIgnoreFile = (file, cwd) => {
  23. const base = slash(path.relative(cwd, path.dirname(file.filePath)));
  24. return file.content
  25. .split(/\r?\n/)
  26. .filter(line => line && !line.startsWith('#'))
  27. .map(pattern => applyBaseToPattern(pattern, base));
  28. };
  29. const toRelativePath = (fileOrDirectory, cwd) => {
  30. cwd = slash(cwd);
  31. if (path.isAbsolute(fileOrDirectory)) {
  32. if (slash(fileOrDirectory).startsWith(cwd)) {
  33. return path.relative(cwd, fileOrDirectory);
  34. }
  35. throw new Error(`Path ${fileOrDirectory} is not in cwd ${cwd}`);
  36. }
  37. return fileOrDirectory;
  38. };
  39. const getIsIgnoredPredicate = (files, cwd) => {
  40. const patterns = files.flatMap(file => parseIgnoreFile(file, cwd));
  41. const ignores = gitIgnore().add(patterns);
  42. return fileOrDirectory => {
  43. fileOrDirectory = toPath(fileOrDirectory);
  44. fileOrDirectory = toRelativePath(fileOrDirectory, cwd);
  45. return fileOrDirectory ? ignores.ignores(slash(fileOrDirectory)) : false;
  46. };
  47. };
  48. const normalizeOptions = (options = {}) => ({
  49. cwd: toPath(options.cwd) || process.cwd(),
  50. suppressErrors: Boolean(options.suppressErrors),
  51. });
  52. export const isIgnoredByIgnoreFiles = async (patterns, options) => {
  53. const {cwd, suppressErrors} = normalizeOptions(options);
  54. const paths = await fastGlob(patterns, {cwd, suppressErrors, ...ignoreFilesGlobOptions});
  55. const files = await Promise.all(
  56. paths.map(async filePath => ({
  57. filePath,
  58. content: await fs.promises.readFile(filePath, 'utf8'),
  59. })),
  60. );
  61. return getIsIgnoredPredicate(files, cwd);
  62. };
  63. export const isIgnoredByIgnoreFilesSync = (patterns, options) => {
  64. const {cwd, suppressErrors} = normalizeOptions(options);
  65. const paths = fastGlob.sync(patterns, {cwd, suppressErrors, ...ignoreFilesGlobOptions});
  66. const files = paths.map(filePath => ({
  67. filePath,
  68. content: fs.readFileSync(filePath, 'utf8'),
  69. }));
  70. return getIsIgnoredPredicate(files, cwd);
  71. };
  72. export const isGitIgnored = options => isIgnoredByIgnoreFiles(GITIGNORE_FILES_PATTERN, options);
  73. export const isGitIgnoredSync = options => isIgnoredByIgnoreFilesSync(GITIGNORE_FILES_PATTERN, options);