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

ISSUE-15: The «markDirectories» option #30

Merged
merged 3 commits into from
Jan 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
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: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ Follow symlinked directories when expanding `**` patterns.

Prevent duplicate results.

#### markDirectories

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

Add a `/` character to directory entries.

#### nobrace

* Type: `boolean`
Expand Down Expand Up @@ -254,6 +261,7 @@ Not fully, because `fast-glob` does not implement all options of `node-glob`. Se
| `root` | – |
| `dot` | [`dot`](#dot) |
| `nomount` | – |
| `mark` | [`markDirectories`](#markdirectories) |
| `nosort` | – |
| `nounique` | [`unique`](#unique) |
| `nobrace` | [`nobrace`](#nobrace) |
Expand Down
2 changes: 2 additions & 0 deletions src/managers/options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('Managers → Options', () => {
onlyDirectories: false,
followSymlinkedDirectories: true,
unique: true,
markDirectories: false,
nobrace: false,
noglobstar: false,
noext: false,
Expand All @@ -39,6 +40,7 @@ describe('Managers → Options', () => {
onlyDirectories: false,
followSymlinkedDirectories: true,
unique: true,
markDirectories: false,
nobrace: false,
noglobstar: false,
noext: false,
Expand Down
5 changes: 5 additions & 0 deletions src/managers/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export interface IOptions<T = EntryItem> {
* Prevent duplicate results.
*/
unique: boolean;
/**
* Add a `/` character to directory entries.
*/
markDirectories: boolean;
/**
* Disable expansion of brace patterns.
*/
Expand Down Expand Up @@ -82,6 +86,7 @@ export function prepare(options?: IPartialOptions): IOptions {
onlyDirectories: false,
followSymlinkedDirectories: true,
unique: true,
markDirectories: false,
nobrace: false,
noglobstar: false,
noext: false,
Expand Down
6 changes: 1 addition & 5 deletions src/providers/reader-async.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ import { ITask } from '../managers/tasks';
import { Entry, EntryItem } from '../types/entries';

class ReaderAsyncFake extends ReaderAsync {
public apiWithStat(): NodeJS.ReadableStream {
return this.fake({ path: 'fake' } as Entry);
}

public api(): NodeJS.ReadableStream {
return this.fake('fake');
return this.fake({ path: 'fake' } as Entry);
}

public fake(value: EntryItem, error?: Error | null): NodeJS.ReadableStream {
Expand Down
26 changes: 4 additions & 22 deletions src/providers/reader-async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,14 @@ import * as readdir from 'readdir-enhanced';
import Reader from './reader';

import { ITask } from '../managers/tasks';
import { EntryItem } from '../types/entries';
import { Entry, EntryItem } from '../types/entries';

export default class ReaderAsync extends Reader {
/**
* Returns founded paths with fs.Stats.
*/
public apiWithStat(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
return readdir.readdirStreamStat(root, options);
}

/**
* Returns founded paths.
*/
public api(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
return readdir.stream(root, options);
}

/**
* Returns stream.
*/
public getStream(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
if (this.options.stats) {
return this.apiWithStat(root, options);
}

return this.api(root, options);
return readdir.readdirStreamStat(root, options);
}

/**
Expand All @@ -41,14 +23,14 @@ export default class ReaderAsync extends Reader {
const entries: EntryItem[] = [];

return new Promise((resolve, reject) => {
const stream: NodeJS.ReadableStream = this.getStream(root, options);
const stream: NodeJS.ReadableStream = this.api(root, options);

stream.on('error', (err) => {
this.isEnoentCodeError(err) ? resolve([]) : reject(err);
stream.pause();
});

stream.on('data', (entry) => entries.push(this.options.transform ? this.options.transform(entry) : entry));
stream.on('data', (entry: Entry) => entries.push(this.transform(entry)));
stream.on('end', () => resolve(entries));
});
}
Expand Down
6 changes: 1 addition & 5 deletions src/providers/reader-stream.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ import { ITask } from '../managers/tasks';
import { Entry, EntryItem } from '../types/entries';

class ReaderStreamFake extends ReaderStream {
public apiWithStat(): NodeJS.ReadableStream {
return this.fake({ path: 'fake' } as Entry);
}

public api(): NodeJS.ReadableStream {
return this.fake('fake');
return this.fake({ path: 'fake' } as Entry);
}

public fake(value: EntryItem, error?: Error | null): NodeJS.ReadableStream {
Expand Down
37 changes: 8 additions & 29 deletions src/providers/reader-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,25 @@ import * as readdir from 'readdir-enhanced';

import Reader from './reader';

import { IOptions } from '../managers/options';
import { ITask } from '../managers/tasks';
import { EntryItem } from '../types/entries';
import { Entry } from '../types/entries';

class TransformStream extends stream.Transform {
constructor(private readonly options: IOptions) {
super(options.stats ? { objectMode: true } : { encoding: 'utf-8' });
constructor(private readonly reader: ReaderStream) {
super({ objectMode: true });
}

public _transform(chunk: string | Buffer, _encoding: string, callback: Function): void {
const entry: EntryItem = Buffer.isBuffer(chunk) ? chunk.toString() : chunk;

callback(null, this.options.transform === null ? entry : this.options.transform(entry));
public _transform(entry: Entry, _encoding: string, callback: Function): void {
callback(null, this.reader.transform(entry));
}
}

export default class ReaderStream extends Reader {
/**
* Returns founded paths with fs.Stats.
*/
public apiWithStat(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
return readdir.readdirStreamStat(root, options);
}

/**
* Returns founded paths.
*/
public api(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
return readdir.stream(root, options);
}

/**
* Returns stream.
*/
public getStream(root: string, options: readdir.IReaddirOptions): NodeJS.ReadableStream {
if (this.options.stats) {
return this.apiWithStat(root, options);
}

return this.api(root, options);
return readdir.readdirStreamStat(root, options);
}

/**
Expand All @@ -52,9 +31,9 @@ export default class ReaderStream extends Reader {
public read(task: ITask): NodeJS.ReadableStream {
const root = this.getRootDirectory(task);
const options = this.getReaderOptions(task);
const transform = new TransformStream(this.options);
const transform = new TransformStream(this);

const readable: NodeJS.ReadableStream = this.getStream(root, options);
const readable: NodeJS.ReadableStream = this.api(root, options);

return readable
.once('error', (err) => this.isEnoentCodeError(err) ? null : transform.emit('error', err))
Expand Down
6 changes: 1 addition & 5 deletions src/providers/reader-sync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ import { ITask } from '../managers/tasks';
import { Entry } from '../types/entries';

class ReaderSyncFake extends ReaderSync {
public apiWithStat(): Entry[] {
public api(): Entry[] {
return [{ path: 'fake' } as Entry];
}

public api(): string[] {
return ['fake'];
}
}

class ReaderSyncFakeThrowEnoent extends ReaderSyncFake {
Expand Down
26 changes: 4 additions & 22 deletions src/providers/reader-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,11 @@ import { ITask } from '../managers/tasks';
import { Entry, EntryItem } from '../types/entries';

export default class ReaderSync extends Reader {
/**
* Returns founded paths with fs.Stats.
*/
public apiWithStat(root: string, options: readdir.IReaddirOptions): Entry[] {
return readdir.readdirSyncStat(root, options);
}

/**
* Returns founded paths.
*/
public api(root: string, options: readdir.IReaddirOptions): string[] {
return readdir.sync(root, options);
}

/**
* Returns entries.
*/
public getEntries(root: string, options: readdir.IReaddirOptions): EntryItem[] {
if (this.options.stats) {
return this.apiWithStat(root, options);
}

return this.api(root, options);
public api(root: string, options: readdir.IReaddirOptions): Entry[] {
return readdir.readdirSyncStat(root, options);
}

/**
Expand All @@ -39,9 +21,9 @@ export default class ReaderSync extends Reader {
const options = this.getReaderOptions(task);

try {
const entries: EntryItem[] = this.getEntries(root, options);
const entries: Entry[] = this.api(root, options);

return this.options.transform === null ? entries : entries.map<EntryItem>(this.options.transform);
return entries.map<EntryItem>(this.transform, this);
} catch (err) {
if (this.isEnoentCodeError(err)) {
return [];
Expand Down
66 changes: 66 additions & 0 deletions src/providers/reader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,72 @@ describe('Providers → Reader', () => {
});
});

describe('.transform', () => {
describe('The «markDirectories» option', () => {
it('should return mark directory when option is enabled', () => {
const reader = getReader({ markDirectories: true });

const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */);

const expected: string = 'fixtures/directory/';

const actual = reader.transform(entry);

assert.equal(actual, expected);
});

it('should do nothing with file when option is enabled', () => {
const reader = getReader({ markDirectories: true });

const entry = getFileEntry(false /** dot */);

const expected: string = 'fixtures/file.txt';

const actual = reader.transform(entry);

assert.equal(actual, expected);
});

it('should return non-marked directory when option is disabled', () => {
const reader = getReader();

const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */);

const expected: string = 'fixtures/directory';

const actual = reader.transform(entry);

assert.equal(actual, expected);
});
});

describe('The «transform» option', () => {
it('should return transformed entry when option is provided', () => {
const reader = getReader({ transform: () => 'cake' });

const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */);

const expected: string = 'cake';

const actual = reader.transform(entry);

assert.equal(actual, expected);
});

it('should return do nothing when option is not provided', () => {
const reader = getReader();

const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */);

const expected: string = 'fixtures/directory';

const actual = reader.transform(entry);

assert.equal(actual, expected);
});
});
});

describe('.isEnoentCodeError', () => {
it('should return true for ENOENT error', () => {
const reader = getReader();
Expand Down
18 changes: 18 additions & 0 deletions src/providers/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IOptions } from '../managers/options';
import { ITask } from '../managers/tasks';

import { IEntry, IReaddirOptions } from 'readdir-enhanced';
import { Entry, EntryItem } from '../types/entries';
import { Pattern } from '../types/patterns';

export default abstract class Reader {
Expand Down Expand Up @@ -117,6 +118,23 @@ export default abstract class Reader {
return !micromatch.any(entry.path, negative, this.micromatchOptions);
}

/**
* Returns transformed entry.
*/
public transform(entry: Entry): EntryItem {
if (this.options.markDirectories && entry.isDirectory()) {
entry.path += '/';
}

const item: EntryItem = this.options.stats ? entry : entry.path;

if (this.options.transform === null) {
return item;
}

return this.options.transform(item);
}

/**
* Returns true if error has ENOENT code.
*/
Expand Down