Skip to content

Commit

Permalink
Merge pull request #200 from mrmlnc/fix_some_issues
Browse files Browse the repository at this point in the history
ISSUE-181: fix some issues
  • Loading branch information
mrmlnc authored Jun 9, 2019
2 parents 3be96c9 + b32842d commit f3642dc
Show file tree
Hide file tree
Showing 21 changed files with 86 additions and 171 deletions.
45 changes: 10 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ The current working directory in which to search.

#### deep

* Type: `number|boolean`
* Default: `true`
* Type: `number`
* Default: `Infinity`

The deep option can be set to `true` to traverse the entire directory structure, or it can be set to a *number* to only traverse that many levels deep. The countdown begins with `1`.
The deep option can be set to `Infinity` to traverse the entire directory structure, or it can be set to a *number* to only traverse that many levels deep. The countdown begins with `1`.

For example, you have the following tree:

Expand Down Expand Up @@ -180,6 +180,13 @@ An array of glob patterns to exclude matches.

Allow patterns to match filenames starting with a period (files & directories), even if the pattern does not explicitly have a period in that spot.

#### objectMode

* Type: `boolean`
* Default: `false`

Return an [`Entry`](#entry) object instead of filepath.

#### stats

* Type: `boolean`
Expand Down Expand Up @@ -291,38 +298,6 @@ Allow glob patterns without slashes to match a file path based on its basename.
Suppress any errors from reader. Works only with Node.js 10.10+.
Can be useful when the directory has entries with a special level of access.

#### transform

* Type: `Function`
* Default: `null`

Allows you to transform a path or `fs.Stats` object before sending to the array.

```js
const fg = require('fast-glob');

const entries1 = fg.sync(['**/*.scss']);
const entries2 = fg.sync(['**/*.scss'], { transform: (entry) => '_' + entry });

console.log(entries1); // ['a.scss', 'b.scss']
console.log(entries2); // ['_a.scss', '_b.scss']
```

If you are using **TypeScript**, you probably want to specify your own type of the returned array.

```ts
import * as fg from 'fast-glob';

interface IMyOwnEntry {
path: string;
}

const entries: IMyOwnEntry[] = fg.sync<IMyOwnEntry>(['*.md'], {
transform: (entry) => typeof entry === 'string' ? { path: entry } : { path: entry.path }
// Will throw compilation error for non-IMyOwnEntry types (boolean, for example)
});
```

#### fs

* Type: `FileSystemAdapter`
Expand Down
24 changes: 18 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,50 @@ import ProviderAsync from './providers/async';
import Provider from './providers/provider';
import ProviderStream from './providers/stream';
import ProviderSync from './providers/sync';
import Settings, { Options, TransformFunction } from './settings';
import { EntryItem, Pattern } from './types/index';
import Settings, { Options } from './settings';
import { Entry, EntryItem, Pattern } from './types/index';
import * as utils from './utils/index';

type Task = taskManager.Task;

type EntryObjectModePredicate = { [P in keyof Pick<Options, 'objectMode'>]-?: true };
type EntryStatsPredicate = { [P in keyof Pick<Options, 'stats'>]-?: true };
type EntryObjectPredicate = EntryObjectModePredicate | EntryStatsPredicate;

function sync(source: Pattern | Pattern[], options: Options & EntryObjectPredicate): Entry[];
function sync(source: Pattern | Pattern[], options?: Options): string[];
function sync(source: Pattern | Pattern[], options?: Options): EntryItem[] {
assertPatternsInput(source);

const works = getWorks<EntryItem[]>(source, ProviderSync, options);
const works = getWorks(source, ProviderSync, options);

return utils.array.flatten(works);
}

function async(source: Pattern | Pattern[], options: Options & EntryObjectPredicate): Promise<Entry[]>;
function async(source: Pattern | Pattern[], options?: Options): Promise<string[]>;
function async(source: Pattern | Pattern[], options?: Options): Promise<EntryItem[]> {
try {
assertPatternsInput(source);
} catch (error) {
return Promise.reject(error);
}

const works = getWorks<Promise<EntryItem[]>>(source, ProviderAsync, options);
const works = getWorks(source, ProviderAsync, options);

return Promise.all(works).then(utils.array.flatten);
}

function stream(source: Pattern | Pattern[], options?: Options): NodeJS.ReadableStream {
assertPatternsInput(source);

const works = getWorks<NodeJS.ReadableStream>(source, ProviderStream, options);
const works = getWorks(source, ProviderStream, options);

/**
* The stream returned by the provider cannot work with an asynchronous iterator.
* To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams.
* This affects performance (+25%). I don't see best solution right now.
*/
return utils.stream.merge(works);
}

Expand Down Expand Up @@ -78,7 +91,6 @@ export {

Options,
Settings,
TransformFunction,
Task,
EntryItem
};
11 changes: 0 additions & 11 deletions src/providers/async.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,6 @@ describe('Providers → ProviderAsync', () => {
assert.deepStrictEqual(actual, expected);
});

it('should call the transform function when it is presented', async () => {
const transform = sinon.stub();
const provider = getProvider({ transform });
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();

await getEntries(provider, task, entry);

assert.strictEqual(transform.callCount, 1);
});

it('should throw error', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
Expand Down
4 changes: 2 additions & 2 deletions src/providers/filters/deep.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ describe('Providers → Filters → Deep', () => {

describe('.getFilter', () => {
describe('options.deep', () => {
it('should return `false` when option is disabled', () => {
const filter = getFilter('.', ['**/*'], [], { deep: false });
it('should return `false` when option has 0 as value', () => {
const filter = getFilter('.', ['**/*'], [], { deep: 0 });
const entry = tests.entry.builder().path('root/directory').directory().build();

const actual = filter(entry);
Expand Down
2 changes: 1 addition & 1 deletion src/providers/filters/deep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default class DeepFilter {
}

private _isSkippedByDeep(entryDepth: number): boolean {
return !this._settings.deep || (typeof this._settings.deep === 'number' && entryDepth >= this._settings.deep);
return entryDepth >= this._settings.deep;
}

private _isSkippedByMaxPatternDepth(entryDepth: number, maxPatternDepth: number): boolean {
Expand Down
3 changes: 2 additions & 1 deletion src/providers/provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ describe('Providers → Provider', () => {
nobrace: false,
nocase: false,
noext: false,
noglobstar: false
noglobstar: false,
posix: true
};

const actual = provider.getMicromatchOptions();
Expand Down
7 changes: 4 additions & 3 deletions src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ export default abstract class Provider<T> {
protected _getMicromatchOptions(): MicromatchOptions {
return {
dot: this._settings.dot,
matchBase: this._settings.matchBase,
nobrace: !this._settings.braceExpansion,
noglobstar: !this._settings.globstar,
noext: !this._settings.extglob,
nocase: !this._settings.caseSensitiveMatch,
matchBase: this._settings.matchBase
noext: !this._settings.extglob,
noglobstar: !this._settings.globstar,
posix: true
};
}
}
11 changes: 0 additions & 11 deletions src/providers/stream.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,6 @@ describe('Providers → ProviderStream', () => {
assert.deepStrictEqual(actual, expected);
});

it('should call the transform function when it is presented', async () => {
const transform = sinon.stub();
const provider = getProvider({ transform });
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();

await getEntries(provider, task, entry);

assert.strictEqual(transform.callCount, 1);
});

it('should emit error to the transform stream', (done) => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
Expand Down
13 changes: 0 additions & 13 deletions src/providers/sync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,5 @@ describe('Providers → ProviderSync', () => {
assert.strictEqual(provider.reader.static.callCount, 1);
assert.deepStrictEqual(actual, expected);
});

it('should call the transform function when it is presented', () => {
const transform = sinon.stub();
const provider = getProvider({ transform });
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();

provider.reader.dynamic.returns([entry]);

provider.read(task);

assert.strictEqual(transform.callCount, 1);
});
});
});
2 changes: 1 addition & 1 deletion src/providers/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class ProviderSync extends Provider<EntryItem[]> {

const entries = this.api(root, task, options);

return entries.map<EntryItem>(options.transform);
return entries.map(options.transform);
}

public api(root: string, task: Task, options: ReaderOptions): Entry[] {
Expand Down
16 changes: 8 additions & 8 deletions src/providers/transformers/entry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,26 @@ describe('Providers → Transformers → Entry', () => {
assert.strictEqual(actual, expected);
});

it('should return transformed entry as object when the `stats` option is enabled', () => {
const transformer = getTransformer({ stats: true });
it('should return transformed entry as object when the `objectMode` option is enabled', () => {
const transformer = getTransformer({ objectMode: true });
const entry = tests.entry.builder().path('root/file.txt').file().build();

const expected = entry;

const actual = transformer(entry);

assert.deepEqual(actual, expected);
assert.deepStrictEqual(actual, expected);
});

it('should apply custom transform function', () => {
const transformer = getTransformer({ transform: () => 'cake' });
const entry = tests.entry.builder().path('root/file.txt').file().build();
it('should return transformed entry as object when the `stats` option is enabled', () => {
const transformer = getTransformer({ stats: true });
const entry = tests.entry.builder().path('root/file.txt').file().stats().build();

const expected = 'cake';
const expected = entry;

const actual = transformer(entry);

assert.strictEqual(actual, expected);
assert.deepStrictEqual(actual, expected);
});

it('should return entry with absolute filepath when the `absolute` option is enabled', () => {
Expand Down
8 changes: 3 additions & 5 deletions src/providers/transformers/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ export default class EntryTransformer {

entry.path = utils.path.unixify(entry.path);

const item = this._settings.stats ? entry : entry.path;

if (this._settings.transform === null) {
return item;
if (this._settings.objectMode) {
return entry;
}

return this._settings.transform(item);
return entry.path;
}
}
2 changes: 1 addition & 1 deletion src/readers/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default abstract class Reader<T> {
constructor(protected readonly _settings: Settings) { }

public abstract dynamic(root: string, options: ReaderOptions): T;
public abstract static(filepath: string[], options: ReaderOptions): T;
public abstract static(patterns: Pattern[], options: ReaderOptions): T;

protected _getFullEntryPath(filepath: string): string {
return path.resolve(this._settings.cwd, filepath);
Expand Down
2 changes: 1 addition & 1 deletion src/readers/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class ReaderStream extends Reader<NodeJS.ReadableStream> {
return walk;
}

public static(patterns: string[], options: ReaderOptions): NodeJS.ReadableStream {
public static(patterns: Pattern[], options: ReaderOptions): NodeJS.ReadableStream {
const filepaths = patterns.map(this._getFullEntryPath, this);

const stream = new PassThrough({ objectMode: true });
Expand Down
6 changes: 3 additions & 3 deletions src/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('Settings', () => {
assert.ok(settings.deep);
assert.deepStrictEqual(settings.ignore, []);
assert.ok(!settings.dot);
assert.ok(!settings.objectMode);
assert.ok(!settings.stats);
assert.ok(settings.onlyFiles);
assert.ok(!settings.onlyDirectories);
Expand All @@ -24,7 +25,6 @@ describe('Settings', () => {
assert.ok(settings.extglob);
assert.ok(settings.caseSensitiveMatch);
assert.ok(!settings.matchBase);
assert.strictEqual(settings.transform, null);
assert.ok(!settings.suppressErrors);
assert.deepStrictEqual(settings.fs, DEFAULT_FILE_SYSTEM_ADAPTER);
});
Expand All @@ -46,13 +46,13 @@ describe('Settings', () => {
assert.ok(settings.onlyDirectories);
});

it('should set the "throwErrorOnBrokenSymbolicLink" option to "true" when the "stats" option is enabled', () => {
it('should set the "objectMode" option when the "stats" is enabled', () => {
const settings = new Settings({
stats: true
});

assert.ok(settings.objectMode);
assert.ok(settings.stats);
assert.ok(settings.throwErrorOnBrokenSymbolicLink);
});

it('should return the `fs` option with custom method', () => {
Expand Down
Loading

0 comments on commit f3642dc

Please sign in to comment.