Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ignoreFiles option #172

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions gitignore.js → ignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,21 @@ const normalizeOptions = (options = {}) => ({
cwd: toPath(options.cwd) || slash(process.cwd()),
});

export const isGitIgnored = async options => {
export const isIgnored = async (pattern, options) => {
const {cwd} = normalizeOptions(options);

const paths = await fastGlob('**/.gitignore', {cwd, ...gitignoreGlobOptions});
const paths = await fastGlob(pattern, {cwd, ...gitignoreGlobOptions});

const files = await Promise.all(paths.map(file => getFile(file, cwd)));
const ignores = reduceIgnore(files);

return getIsIgnoredPredicate(ignores, cwd);
};

export const isGitIgnoredSync = options => {
export const isIgnoredSync = (pattern, options) => {
const {cwd} = normalizeOptions(options);

const paths = fastGlob.sync('**/.gitignore', {cwd, ...gitignoreGlobOptions});
const paths = fastGlob.sync(pattern, {cwd, ...gitignoreGlobOptions});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add dot: true to the option, when pattern is *ignore, not sure, need test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add dot: true to the option, when pattern is *ignore

Sorry, I don't understand the suggestion.

need test.

Added.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, if user use {ignoreFiles: '*ignore'}, can this glob files starts with .? See https://github.com/mrmlnc/fast-glob#dot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't find that necessary, and it makes the configuration a little complicated (what if the user didn't want to use dotfiles?). But I'll leave the decision to you.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a maintainer :)


const files = paths.map(file => getFileSync(file, cwd));
const ignores = reduceIgnore(files);
Expand Down
9 changes: 9 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,20 @@ export interface Options extends FastGlobOptionsWithoutCwd {

/**
Respect ignore patterns in `.gitignore` files that apply to the globbed files.
This option takes precedence over the `ignoreFiles` option.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This don't have to "takes precedence", we can concat **/.gitignore with ignoreFiles, so they can use together.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The content in readme.md file didn't fix.


@default false
*/
readonly gitignore?: boolean;

/**
A glob pattern to look for ignore files, which are then used to ignore globbed files. This is a more generic form of the `gitignore` option, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
This value is unused if `{ gitignore: true }`, since `gitignore` takes precedence.

@default undefined
*/
readonly ignoreFiles?: string;
jridgewell marked this conversation as resolved.
Show resolved Hide resolved

/**
The current working directory in which to search.

Expand Down
29 changes: 18 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs';
import merge2 from 'merge2';
import fastGlob from 'fast-glob';
import dirGlob from 'dir-glob';
import {isGitIgnored, isGitIgnoredSync} from './gitignore.js';
import {isIgnored, isIgnoredSync} from './ignore.js';
import {FilterStream, toPath} from './utilities.js';

const isNegative = pattern => pattern[0] === '!';
Expand Down Expand Up @@ -52,13 +52,21 @@ const normalizeOptions = (options = {}) => {
const normalizeArguments = fn => async (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));
const normalizeArgumentsSync = fn => (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));

const getFilter = async options => createFilterFunction(
options.gitignore && await isGitIgnored({cwd: options.cwd}),
);
const getIgnoreFilesPattern = options => options && options.gitignore ? '**/.gitignore' : options && options.ignoreFiles;

const getFilterSync = options => createFilterFunction(
options.gitignore && isGitIgnoredSync({cwd: options.cwd}),
);
const getFilter = async options => {
const ignoreFilesPattern = getIgnoreFilesPattern(options);
return createFilterFunction(
ignoreFilesPattern && await isIgnored(ignoreFilesPattern, {cwd: options.cwd}),
);
};

const getFilterSync = options => {
const ignoreFilesPattern = getIgnoreFilesPattern(options);
return createFilterFunction(
ignoreFilesPattern && isIgnoredSync(ignoreFilesPattern, {cwd: options.cwd}),
);
};

const createFilterFunction = isIgnored => {
const seen = new Set();
Expand Down Expand Up @@ -200,7 +208,6 @@ export const isDynamicPattern = normalizeArgumentsSync(
export const generateGlobTasks = normalizeArguments(generateTasks);
export const generateGlobTasksSync = normalizeArgumentsSync(generateTasksSync);

export {
isGitIgnored,
isGitIgnoredSync,
} from './gitignore.js';
export {isIgnored, isIgnoredSync} from './ignore.js';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we want to expose these, if we do, maybe we need a better name, isIgnoredByIgnoreFiles?

export const isGitIgnored = options => isIgnored('**/.gitignore', options);
export const isGitIgnoredSync = options => isIgnoredSync('**/.gitignore', options);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"files": [
"index.js",
"index.d.ts",
"gitignore.js",
"ignore.js",
"utilities.js"
],
"keywords": [
Expand Down
13 changes: 12 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Based on [`fast-glob`](https://github.com/mrmlnc/fast-glob) but adds a bunch of
- Multiple patterns
- Negated patterns: `['foo*', '!foobar']`
- Expands directories: `foo` → `foo/**/*`
- Supports `.gitignore`
- Supports `.gitignore`, or any similar ignore file configuration.
- Supports `URL` as `cwd`

## Install
Expand Down Expand Up @@ -88,6 +88,17 @@ Default: `false`

Respect ignore patterns in `.gitignore` files that apply to the globbed files.

This option takes precedence over the `ignoreFiles` option.

##### ignoreFiles

Type: `string`\
Default: `undefined`

A glob pattern to look for ignore files, which are then used to ignore globbed files. This is a more generic form of the `gitignore` option, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.

This value is unused if `{ gitignore: true }`, because `gitignore` takes precedence.

### globbySync(patterns, options?)

Returns `string[]` of matching paths.
Expand Down
58 changes: 30 additions & 28 deletions tests/gitignore.js → tests/ignore.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,65 @@
import path from 'node:path';
import test from 'ava';
import slash from 'slash';
import {isGitIgnored, isGitIgnoredSync} from '../gitignore.js';
import {isIgnored, isIgnoredSync} from '../ignore.js';
import {
PROJECT_ROOT,
getPathValues,
} from './utilities.js';

test('gitignore', async t => {
const gitignorePattern = '**/.gitignore';

test('ignore', async t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/gitignore'))) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const ignored = await isIgnored(gitignorePattern, {cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !ignored(file));
const expected = ['bar.js'];
t.deepEqual(actual, expected);
}
});

test('gitignore - mixed path styles', async t => {
test('ignore - mixed path styles', async t => {
const directory = path.join(PROJECT_ROOT, 'fixtures/gitignore');
for (const cwd of getPathValues(directory)) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});
t.true(isIgnored(slash(path.resolve(directory, 'foo.js'))));
const ignored = await isIgnored(gitignorePattern, {cwd});
t.true(ignored(slash(path.resolve(directory, 'foo.js'))));
}
});

test('gitignore - os paths', async t => {
test('ignore - os paths', async t => {
const directory = path.join(PROJECT_ROOT, 'fixtures/gitignore');
for (const cwd of getPathValues(directory)) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});
t.true(isIgnored(path.resolve(directory, 'foo.js')));
const ignored = await isIgnored(gitignorePattern, {cwd});
t.true(ignored(path.resolve(directory, 'foo.js')));
}
});

test('gitignore - sync', t => {
test('ignore - sync', t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/gitignore'))) {
const isIgnored = isGitIgnoredSync({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const ignored = isIgnoredSync(gitignorePattern, {cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !ignored(file));
const expected = ['bar.js'];
t.deepEqual(actual, expected);
}
});

test('negative gitignore', async t => {
test('negative ignore', async t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/negative'))) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const ignored = await isIgnored(gitignorePattern, {cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !ignored(file));
const expected = ['foo.js'];
t.deepEqual(actual, expected);
}
});

test('negative gitignore - sync', t => {
test('negative ignore - sync', t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/negative'))) {
const isIgnored = isGitIgnoredSync({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const ignored = isIgnoredSync(gitignorePattern, {cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !ignored(file));
const expected = ['foo.js'];
t.deepEqual(actual, expected);
}
Expand All @@ -66,14 +68,14 @@ test('negative gitignore - sync', t => {
test('multiple negation', async t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/multiple-negation'))) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});
const ignored = await isIgnored(gitignorePattern, {cwd});

const actual = [
'!!!unicorn.js',
'!!unicorn.js',
'!unicorn.js',
'unicorn.js',
].filter(file => !isIgnored(file));
].filter(file => !ignored(file));

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
Expand All @@ -82,14 +84,14 @@ test('multiple negation', async t => {

test('multiple negation - sync', t => {
for (const cwd of getPathValues(path.join(PROJECT_ROOT, 'fixtures/multiple-negation'))) {
const isIgnored = isGitIgnoredSync({cwd});
const ignored = isIgnoredSync(gitignorePattern, {cwd});

const actual = [
'!!!unicorn.js',
'!!unicorn.js',
'!unicorn.js',
'unicorn.js',
].filter(file => !isIgnored(file));
].filter(file => !ignored(file));

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
Expand All @@ -99,15 +101,15 @@ test('multiple negation - sync', t => {
test('check file', async t => {
const directory = path.join(PROJECT_ROOT, 'fixtures/gitignore');
const ignoredFile = path.join(directory, 'foo.js');
const isIgnored = await isGitIgnored({cwd: directory});
const isIgnoredSync = isGitIgnoredSync({cwd: directory});
const ignored = await isIgnored(gitignorePattern, {cwd: directory});
const ignoredSync = isIgnoredSync(gitignorePattern, {cwd: directory});

for (const file of getPathValues(ignoredFile)) {
t.true(isIgnored(file));
t.true(isIgnoredSync(file));
t.true(ignored(file));
t.true(ignoredSync(file));
}

for (const file of getPathValues(path.join(directory, 'bar.js'))) {
t.false(isIgnored(file));
t.false(ignored(file));
}
});