Skip to content

Commit

Permalink
include gitignore files from parent dirs
Browse files Browse the repository at this point in the history
  • Loading branch information
dflupu committed Jan 8, 2020
1 parent 45ac58a commit 8159ac0
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 23 deletions.
2 changes: 2 additions & 0 deletions fixtures/gitignore/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
nested-1/foo.js
nested-1/nested-2/foo.js
foo.js
!bar.js
3 changes: 3 additions & 0 deletions fixtures/gitignore/nested-1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nested-2/bar.js
nested-3/
nested-4/
Empty file.
77 changes: 55 additions & 22 deletions gitignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const path = require('path');
const fastGlob = require('fast-glob');
const gitIgnore = require('ignore');
const slash = require('slash');
const findUp = require('find-up');
const findUpAll = require('find-up-all');

const DEFAULT_IGNORE = [
'**/node_modules/**',
Expand Down Expand Up @@ -55,8 +57,12 @@ const ensureAbsolutePathForCwd = (cwd, p) => {
return path.join(cwd, p);
};

const getIsIgnoredPredecate = (ignores, cwd) => {
return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p))));
const getIsIgnoredPredecate = (ignores, gitRoot, cwd) => {
return p => {
const pathWithCwd = ensureAbsolutePathForCwd(cwd, p);
const pathRelativeToGitRoot = path.relative(gitRoot, pathWithCwd);
return ignores.ignores(slash(pathRelativeToGitRoot));
};
};

const getFile = async (file, cwd) => {
Expand Down Expand Up @@ -90,28 +96,55 @@ const normalizeOptions = ({

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);
const {cwd} = options;

const gitDir = await findUp('.git', {cwd});
const gitRoot = gitDir ? path.dirname(gitDir) : '';

const gitIgnoreFilePaths = await Promise.all([
findUpAll('.gitignore', {
cwd: path.join(cwd, '..'),
end: gitRoot
}),
fastGlob('**/.gitignore', {
ignore: DEFAULT_IGNORE.concat(options.ignore),
absolute: true,
cwd
})
]);

const gitIgnoreFileContents = await Promise.all(gitIgnoreFilePaths
.flat()
.map(p => path.relative(gitRoot, p))
.map(file => getFile(file, gitRoot))
);

const ignores = reduceIgnore(gitIgnoreFileContents);
return getIsIgnoredPredecate(ignores, gitRoot, 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);
const {cwd} = options;

const gitDir = findUp.sync('.git', {cwd});
const gitRoot = gitDir ? path.dirname(gitDir) : '';

const gitIgnoreFilePaths = [
...findUpAll.sync('.gitignore', {
cwd: path.join(cwd, '..'),
end: gitRoot
}),
...fastGlob.sync('**/.gitignore', {
ignore: DEFAULT_IGNORE.concat(options.ignore),
cwd
}).map(p => path.join(cwd, p))
];

const gitIgnoreFileContents = gitIgnoreFilePaths
.map(p => path.relative(gitRoot, p))
.map(file => getFileSync(file, gitRoot));

const ignores = reduceIgnore(gitIgnoreFileContents);
return getIsIgnoredPredecate(ignores, gitRoot, cwd);
};
72 changes: 72 additions & 0 deletions gitignore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,75 @@ test('multiple negation - sync', t => {
const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
});

test('gitignore nested 1 level', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/');
const isIgnored = await gitignore({cwd});
const actual = ['bar.js', 'foo.js'].filter(file => !isIgnored(file));
const expected = ['bar.js'];

t.deepEqual(actual, expected);
});

test('gitignore nested 1 level - sync', t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/');
const isIgnored = gitignore.sync({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const expected = ['bar.js'];

t.deepEqual(actual, expected);
});

test('gitignore nested 2 levels', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-2');
const isIgnored = await gitignore({cwd});
const actual = ['foo.js', 'bar.js', 'baz.js'].filter(file => !isIgnored(file));
const expected = ['baz.js'];

t.deepEqual(actual, expected);
});

test('gitignore nested 2 levels - sync', t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-2');
const isIgnored = gitignore.sync({cwd});
const actual = ['foo.js', 'bar.js', 'baz.js'].filter(file => !isIgnored(file));
const expected = ['baz.js'];

t.deepEqual(actual, expected);
});

test('gitignore nested directory', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-3');
const isIgnored = await gitignore({cwd});
const actual = ['foo.js'].filter(file => !isIgnored(file));
const expected = [];

t.deepEqual(actual, expected);
});

test('gitignore nested directory - sync', t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-3');
const isIgnored = gitignore.sync({cwd});
const actual = ['foo.js'].filter(file => !isIgnored(file));
const expected = [];

t.deepEqual(actual, expected);
});

test('gitignore does not read ignore files above git root directory', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-4');
const isIgnored = await gitignore({cwd});
const actual = ['foo.js'].filter(file => !isIgnored(file));
const expected = ['foo.js'];

t.deepEqual(actual, expected);
});

test('gitignore does not read ignore files above git root directory - sync', t => {
const cwd = path.join(__dirname, 'fixtures/gitignore/nested-1/nested-4');
const isIgnored = gitignore.sync({cwd});
const actual = ['foo.js'].filter(file => !isIgnored(file));
const expected = ['foo.js'];

t.deepEqual(actual, expected);
});
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
"fast-glob": "^3.1.1",
"ignore": "^5.1.4",
"merge2": "^1.3.0",
"slash": "^3.0.0"
"slash": "^3.0.0",
"find-up": "^3.0.0",
"find-up-all": "^1.0.2"
},
"devDependencies": {
"ava": "^2.1.0",
Expand Down

0 comments on commit 8159ac0

Please sign in to comment.