Skip to content

Commit

Permalink
Support URL as cwd (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker committed Jan 15, 2022
1 parent de4082b commit 73c0aca
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 124 deletions.
3 changes: 2 additions & 1 deletion gitignore.js
Expand Up @@ -4,6 +4,7 @@ import path from 'node:path';
import fastGlob from 'fast-glob';
import gitIgnore from 'ignore';
import slash from 'slash';
import toPath from './to-path.js';

const DEFAULT_IGNORE = [
'**/node_modules/**',
Expand Down Expand Up @@ -82,7 +83,7 @@ const getFileSync = (file, cwd) => {
const normalizeOptions = ({
ignore = [],
cwd = slash(process.cwd()),
} = {}) => ({ignore: [...DEFAULT_IGNORE, ...ignore], cwd});
} = {}) => ({ignore: [...DEFAULT_IGNORE, ...ignore], cwd: toPath(cwd)});

export const isGitIgnored = async options => {
options = normalizeOptions(options);
Expand Down
141 changes: 80 additions & 61 deletions gitignore.test.js
@@ -1,101 +1,120 @@
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import {fileURLToPath, pathToFileURL} from 'node:url';
import test from 'ava';
import slash from 'slash';
import {isGitIgnored, isGitIgnoredSync} from './gitignore.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const getCwdValues = cwd => [cwd, pathToFileURL(cwd), pathToFileURL(cwd).href];

test('gitignore', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore');
const isIgnored = await isGitIgnored({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const expected = ['bar.js'];
t.deepEqual(actual, expected);
for (const cwd of getCwdValues(path.join(__dirname, '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 expected = ['bar.js'];
t.deepEqual(actual, expected);
}
});

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

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

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

test('ignore ignored .gitignore', async t => {
const cwd = path.join(__dirname, 'fixtures/gitignore');
const ignore = ['**/.gitignore'];

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

test('ignore ignored .gitignore - sync', t => {
const cwd = path.join(__dirname, 'fixtures/gitignore');
const ignore = ['**/.gitignore'];

const isIgnored = isGitIgnoredSync({cwd, ignore});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const expected = ['foo.js', 'bar.js'];
t.deepEqual(actual, expected);
for (const cwd of getCwdValues(path.join(__dirname, 'fixtures/gitignore'))) {
const isIgnored = isGitIgnoredSync({cwd, ignore});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const expected = ['foo.js', 'bar.js'];
t.deepEqual(actual, expected);
}
});

test('negative gitignore', async t => {
const cwd = path.join(__dirname, 'fixtures/negative');
const isIgnored = await isGitIgnored({cwd});
const actual = ['foo.js', 'bar.js'].filter(file => !isIgnored(file));
const expected = ['foo.js'];
t.deepEqual(actual, expected);
for (const cwd of getCwdValues(path.join(__dirname, '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 expected = ['foo.js'];
t.deepEqual(actual, expected);
}
});

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

test('multiple negation', async t => {
const cwd = path.join(__dirname, 'fixtures/multiple-negation');
const isIgnored = await isGitIgnored({cwd});

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

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
for (const cwd of getCwdValues(path.join(__dirname, 'fixtures/multiple-negation'))) {
// eslint-disable-next-line no-await-in-loop
const isIgnored = await isGitIgnored({cwd});

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

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
}
});

test('multiple negation - sync', t => {
const cwd = path.join(__dirname, 'fixtures/multiple-negation');
const isIgnored = isGitIgnoredSync({cwd});

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

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
for (const cwd of getCwdValues(path.join(__dirname, 'fixtures/multiple-negation'))) {
const isIgnored = isGitIgnoredSync({cwd});

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

const expected = ['!!unicorn.js', '!unicorn.js'];
t.deepEqual(actual, expected);
}
});
22 changes: 19 additions & 3 deletions index.d.ts
Expand Up @@ -12,7 +12,9 @@ export type ExpandDirectoriesOption =
| readonly string[]
| {files?: readonly string[]; extensions?: readonly string[]};

export interface Options extends FastGlobOptions {
type FastGlobOptionsWithoutCwd = Omit<FastGlobOptions, 'cwd'>;

export interface Options extends FastGlobOptionsWithoutCwd {
/**
If set to `true`, `globby` will automatically glob directories for you. If you define an `Array` it will only glob files that matches the patterns inside the `Array`. You can also define an `Object` with `files` and `extensions` like in the example below.
Expand Down Expand Up @@ -43,10 +45,17 @@ export interface Options extends FastGlobOptions {
@default false
*/
readonly gitignore?: boolean;

/**
The current working directory in which to search.
@default process.cwd()
*/
readonly cwd?: URL | string;
}

export interface GitignoreOptions {
readonly cwd?: string;
readonly cwd?: URL | string;
readonly ignore?: readonly string[];
}

Expand Down Expand Up @@ -144,7 +153,14 @@ This function is backed by [`fast-glob`](https://github.com/mrmlnc/fast-glob#isd
*/
export function isDynamicPattern(
patterns: string | readonly string[],
options?: FastGlobOptions
options?: FastGlobOptionsWithoutCwd & {
/**
The current working directory in which to search.
@default process.cwd()
*/
readonly cwd?: URL | string;
}
): boolean;

/**
Expand Down
19 changes: 14 additions & 5 deletions index.js
Expand Up @@ -3,6 +3,7 @@ import arrayUnion from 'array-union';
import merge2 from 'merge2';
import fastGlob from 'fast-glob';
import dirGlob from 'dir-glob';
import toPath from './to-path.js';
import {isGitIgnored, isGitIgnoredSync} from './gitignore.js';
import {FilterStream, UniqueStream} from './stream-utils.js';

Expand All @@ -16,7 +17,7 @@ const assertPatternsInput = patterns => {
}
};

const checkCwdOption = (options = {}) => {
const checkCwdOption = options => {
if (!options.cwd) {
return;
}
Expand All @@ -35,19 +36,21 @@ const checkCwdOption = (options = {}) => {

const getPathString = p => p.stats instanceof fs.Stats ? p.path : p;

export const generateGlobTasks = (patterns, taskOptions) => {
export const generateGlobTasks = (patterns, taskOptions = {}) => {
patterns = arrayUnion([patterns].flat());
assertPatternsInput(patterns);
checkCwdOption(taskOptions);

const globTasks = [];

taskOptions = {
ignore: [],
expandDirectories: true,
...taskOptions,
cwd: toPath(taskOptions.cwd),
};

checkCwdOption(taskOptions);

for (const [index, pattern] of patterns.entries()) {
if (isNegative(pattern)) {
continue;
Expand Down Expand Up @@ -179,8 +182,14 @@ export const globbyStream = (patterns, options) => {
.pipe(uniqueStream);
};

export const isDynamicPattern = (patterns, options) => [patterns].flat()
.some(pattern => fastGlob.isDynamicPattern(pattern, options));
export const isDynamicPattern = (patterns, options = {}) => {
options = {
...options,
cwd: toPath(options.cwd),
};

return [patterns].flat().some(pattern => fastGlob.isDynamicPattern(pattern, options));
};

export {
isGitIgnored,
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -23,7 +23,8 @@
"index.js",
"index.d.ts",
"gitignore.js",
"stream-utils.js"
"stream-utils.js",
"to-path.js"
],
"keywords": [
"all",
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Expand Up @@ -11,6 +11,7 @@ Based on [`fast-glob`](https://github.com/mrmlnc/fast-glob) but adds a bunch of
- Negated patterns: `['foo*', '!foobar']`
- Expands directories: `foo``foo/**/*`
- Supports `.gitignore`
- Supports `URL` as `cwd`

## Install

Expand Down Expand Up @@ -125,7 +126,7 @@ This function is backed by [`fast-glob`](https://github.com/mrmlnc/fast-glob#isd

Returns a `Promise<(path: string) => boolean>` indicating whether a given path is ignored via a `.gitignore` file.

Takes `cwd?: string` and `ignore?: string[]` as options. `.gitignore` files matched by the ignore config are not used for the resulting filter function.
Takes `cwd?: URL | string` and `ignore?: string[]` as options. `.gitignore` files matched by the ignore config are not used for the resulting filter function.

```js
import {isGitIgnored} from 'globby';
Expand Down

0 comments on commit 73c0aca

Please sign in to comment.