gitignore.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. 'use strict';
  2. const fs = require('fs');
  3. const path = require('path');
  4. const fastGlob = require('fast-glob');
  5. const gitIgnore = require('ignore');
  6. const pify = require('pify');
  7. const slash = require('slash');
  8. const DEFAULT_IGNORE = [
  9. '**/node_modules/**',
  10. '**/bower_components/**',
  11. '**/flow-typed/**',
  12. '**/coverage/**',
  13. '**/.git'
  14. ];
  15. const readFileP = pify(fs.readFile);
  16. const mapGitIgnorePatternTo = base => ignore => {
  17. if (ignore.startsWith('!')) {
  18. return '!' + path.posix.join(base, ignore.slice(1));
  19. }
  20. return path.posix.join(base, ignore);
  21. };
  22. const parseGitIgnore = (content, options) => {
  23. const base = slash(path.relative(options.cwd, path.dirname(options.fileName)));
  24. return content
  25. .split(/\r?\n/)
  26. .filter(Boolean)
  27. .filter(line => line.charAt(0) !== '#')
  28. .map(mapGitIgnorePatternTo(base));
  29. };
  30. const reduceIgnore = files => {
  31. return files.reduce((ignores, file) => {
  32. ignores.add(parseGitIgnore(file.content, {
  33. cwd: file.cwd,
  34. fileName: file.filePath
  35. }));
  36. return ignores;
  37. }, gitIgnore());
  38. };
  39. const getIsIgnoredPredecate = (ignores, cwd) => {
  40. return p => ignores.ignores(slash(path.relative(cwd, p)));
  41. };
  42. const getFile = (file, cwd) => {
  43. const filePath = path.join(cwd, file);
  44. return readFileP(filePath, 'utf8')
  45. .then(content => ({
  46. content,
  47. cwd,
  48. filePath
  49. }));
  50. };
  51. const getFileSync = (file, cwd) => {
  52. const filePath = path.join(cwd, file);
  53. const content = fs.readFileSync(filePath, 'utf8');
  54. return {
  55. content,
  56. cwd,
  57. filePath
  58. };
  59. };
  60. const normalizeOptions = (options = {}) => {
  61. const ignore = options.ignore || [];
  62. const cwd = options.cwd || process.cwd();
  63. return {ignore, cwd};
  64. };
  65. module.exports = options => {
  66. options = normalizeOptions(options);
  67. return fastGlob('**/.gitignore', {
  68. ignore: DEFAULT_IGNORE.concat(options.ignore),
  69. cwd: options.cwd
  70. })
  71. .then(paths => Promise.all(paths.map(file => getFile(file, options.cwd))))
  72. .then(files => reduceIgnore(files))
  73. .then(ignores => getIsIgnoredPredecate(ignores, options.cwd));
  74. };
  75. module.exports.sync = options => {
  76. options = normalizeOptions(options);
  77. const paths = fastGlob.sync('**/.gitignore', {
  78. ignore: DEFAULT_IGNORE.concat(options.ignore),
  79. cwd: options.cwd
  80. });
  81. const files = paths.map(file => getFileSync(file, options.cwd));
  82. const ignores = reduceIgnore(files);
  83. return getIsIgnoredPredecate(ignores, options.cwd);
  84. };