diff --git a/src/managers/tasks.spec.ts b/src/managers/tasks.spec.ts index aa3a1d59..ee3d285b 100644 --- a/src/managers/tasks.spec.ts +++ b/src/managers/tasks.spec.ts @@ -25,6 +25,7 @@ describe('Managers → Task', () => { const expected: TaskGroup = { a: { base: 'a', + globstar: false, patterns: ['a/*.txt', 'a/*.md'], positive: ['a/*.txt', 'a/*.md'], negative: [] @@ -42,6 +43,7 @@ describe('Managers → Task', () => { const expected: TaskGroup = { a: { base: 'a', + globstar: false, patterns: ['!a/*.txt', '!a/*.md'], positive: [], negative: ['a/*.txt', 'a/*.md'] @@ -59,6 +61,7 @@ describe('Managers → Task', () => { const positive: TaskGroup = { a: { base: 'a', + globstar: true, patterns: ['a/**/*'], positive: ['a/**/*'], negative: [] @@ -68,6 +71,7 @@ describe('Managers → Task', () => { const negative: TaskGroup = { a: { base: 'a', + globstar: false, patterns: ['!a/**/*.txt'], positive: [], negative: ['a/**/*.txt'] @@ -77,6 +81,7 @@ describe('Managers → Task', () => { const expected: TaskGroup = { a: { base: 'a', + globstar: true, patterns: ['a/**/*', '!a/**/*.txt'], positive: ['a/**/*'], negative: ['a/**/*.txt'] @@ -93,6 +98,7 @@ describe('Managers → Task', () => { it('should returns tasks', () => { const expected: ITask[] = [{ base: 'a', + globstar: true, patterns: ['a/**/*', '!a/**/*.txt'], positive: ['a/**/*'], negative: ['a/**/*.txt'] @@ -118,6 +124,7 @@ describe('Managers → Task', () => { it('should returns one global task', () => { const expected: manager.ITask[] = [{ base: '.', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] @@ -132,6 +139,7 @@ describe('Managers → Task', () => { it('should returns one global task with negative patterns', () => { const expected: manager.ITask[] = [{ base: '.', + globstar: true, patterns: ['**/*', '!**/*.md'], positive: ['**/*'], negative: ['**/*.md'] @@ -146,6 +154,7 @@ describe('Managers → Task', () => { it('should returns one global task with negative patterns from options', () => { const expected: manager.ITask[] = [{ base: '.', + globstar: true, patterns: ['**/*', '!**/*.md'], positive: ['**/*'], negative: ['**/*.md'] @@ -162,6 +171,7 @@ describe('Managers → Task', () => { it('should returns one task', () => { const expected: manager.ITask[] = [{ base: 'a', + globstar: true, patterns: ['a/**/*'], positive: ['a/**/*'], negative: [] @@ -176,6 +186,7 @@ describe('Managers → Task', () => { it('should returns one task with negative patterns', () => { const expected: manager.ITask[] = [{ base: 'a', + globstar: true, patterns: ['a/**/*', '!a/*.md'], positive: ['a/**/*'], negative: ['a/*.md'] @@ -190,6 +201,7 @@ describe('Managers → Task', () => { it('should returns one task without unused negative patterns', () => { const expected: manager.ITask[] = [{ base: 'a', + globstar: true, patterns: ['a/**/*'], positive: ['a/**/*'], negative: [] @@ -204,6 +216,7 @@ describe('Managers → Task', () => { it('should returns one task with negative patterns from options', () => { const expected: manager.ITask[] = [{ base: 'a', + globstar: true, patterns: ['a/**/*', '!a/*.md'], positive: ['a/**/*'], negative: ['a/*.md'] @@ -221,12 +234,14 @@ describe('Managers → Task', () => { const expected: manager.ITask[] = [ { base: 'a', + globstar: false, patterns: ['a/*', '!a/*.md'], positive: ['a/*'], negative: ['a/*.md'] }, { base: 'b', + globstar: false, patterns: ['b/*'], positive: ['b/*'], negative: [] @@ -243,12 +258,14 @@ describe('Managers → Task', () => { const expected: manager.ITask[] = [ { base: 'a', + globstar: false, patterns: ['a/*', '!a/*.md', '!**/*.txt'], positive: ['a/*'], negative: ['a/*.md', '**/*.txt'] }, { base: 'b', + globstar: false, patterns: ['b/*', '!**/*.txt'], positive: ['b/*'], negative: ['**/*.txt'] @@ -265,12 +282,14 @@ describe('Managers → Task', () => { const expected: manager.ITask[] = [ { base: 'a', + globstar: false, patterns: ['a/*', '!a/*.md', '!**/*.txt'], positive: ['a/*'], negative: ['a/*.md', '**/*.txt'] }, { base: 'b', + globstar: false, patterns: ['b/*', '!**/*.txt'], positive: ['b/*'], negative: ['**/*.txt'] diff --git a/src/managers/tasks.ts b/src/managers/tasks.ts index 67c3cc2b..4d037288 100644 --- a/src/managers/tasks.ts +++ b/src/managers/tasks.ts @@ -6,6 +6,7 @@ import { IOptions } from './options'; export interface ITask { base: string; + globstar: boolean; patterns: Pattern[]; positive: Pattern[]; negative: Pattern[]; @@ -39,6 +40,7 @@ export function makePositiveTaskGroup(positive: PatternsGroup): TaskGroup { collection[base] = { base, + globstar: positivePatterns.some(patternUtils.hasGlobStar), patterns: positivePatterns, positive: positivePatterns, negative: [] @@ -57,6 +59,7 @@ export function makeNegativeTaskGroup(negative: PatternsGroup): TaskGroup { collection[base] = { base, + globstar: false, patterns: negativePatterns.map(patternUtils.convertToNegativePattern), positive: [], negative: negativePatterns @@ -124,6 +127,7 @@ export function generate(patterns: Pattern[], options: IOptions): ITask[] { if ('.' in positiveGroup) { const task: ITask = { base: '.', + globstar: positive.some(patternUtils.hasGlobStar), patterns: ([] as Pattern[]).concat(positive, negative.map(patternUtils.convertToNegativePattern)), positive, negative diff --git a/src/providers/reader-async.spec.ts b/src/providers/reader-async.spec.ts index d0f435ae..938de443 100644 --- a/src/providers/reader-async.spec.ts +++ b/src/providers/reader-async.spec.ts @@ -46,6 +46,7 @@ describe('Providers → ReaderAsync', () => { describe('.read', () => { const task: ITask = { base: 'fixtures', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] diff --git a/src/providers/reader-stream.spec.ts b/src/providers/reader-stream.spec.ts index 22d3122d..a1ebd26d 100644 --- a/src/providers/reader-stream.spec.ts +++ b/src/providers/reader-stream.spec.ts @@ -64,6 +64,7 @@ describe('Providers → ReaderStream', () => { describe('.read', () => { const task: ITask = { base: 'fixtures', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] diff --git a/src/providers/reader-sync.spec.ts b/src/providers/reader-sync.spec.ts index d126f9ba..e646ab91 100644 --- a/src/providers/reader-sync.spec.ts +++ b/src/providers/reader-sync.spec.ts @@ -41,6 +41,7 @@ describe('Providers → ReaderSync', () => { describe('.read', () => { const task: ITask = { base: 'fixtures', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] diff --git a/src/providers/reader.spec.ts b/src/providers/reader.spec.ts index b69f4e43..c0765c61 100644 --- a/src/providers/reader.spec.ts +++ b/src/providers/reader.spec.ts @@ -86,6 +86,7 @@ describe('Providers → Reader', () => { const actual = reader.getReaderOptions({ base: '.', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] @@ -102,6 +103,7 @@ describe('Providers → Reader', () => { const actual = reader.getReaderOptions({ base: 'fixtures', + globstar: true, patterns: ['**/*'], positive: ['**/*'], negative: [] @@ -392,7 +394,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(!actual); }); @@ -406,7 +408,7 @@ describe('Providers → Reader', () => { isDirectory: () => true }); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(!actual); }); @@ -418,7 +420,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, true /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(actual); }); @@ -428,7 +430,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, true /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(!actual); }); @@ -440,7 +442,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(true /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(actual); }); @@ -450,7 +452,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(true /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(!actual); }); @@ -462,7 +464,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, []); + const actual = reader.deep(entry, [], true /** globstar */); assert.ok(actual); }); @@ -472,7 +474,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, ['**/pony/**']); + const actual = reader.deep(entry, ['**/pony/**'], true /** globstar */); assert.ok(actual); }); @@ -482,7 +484,7 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, ['**/directory/**']); + const actual = reader.deep(entry, ['**/directory/**'], true /** globstar */); assert.ok(actual); }); @@ -492,7 +494,29 @@ describe('Providers → Reader', () => { const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); - const actual = reader.deep(entry, ['**/directory']); + const actual = reader.deep(entry, ['**/directory'], true /** globstar */); + + assert.ok(!actual); + }); + }); + + describe('Filter by «globstar» parameter', () => { + it('should return true by globstar parameter', () => { + const reader = getReader(); + + const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); + + const actual = reader.deep(entry, [], true /** globstar */); + + assert.ok(actual); + }); + + it('should return false by globstar parameter', () => { + const reader = getReader(); + + const entry = getDirectoryEntry(false /** dot */, false /** isSymbolicLink */); + + const actual = reader.deep(entry, [], false /** globstar */); assert.ok(!actual); }); diff --git a/src/providers/reader.ts b/src/providers/reader.ts index b5fdded9..fc5e1ad4 100644 --- a/src/providers/reader.ts +++ b/src/providers/reader.ts @@ -37,7 +37,7 @@ export default abstract class Reader { return { basePath: task.base === '.' ? '' : task.base, filter: (entry) => this.filter(entry, task.patterns, task.negative), - deep: (entry) => this.deep(entry, task.negative), + deep: (entry) => this.deep(entry, task.negative, task.globstar), sep: '/' }; } @@ -93,7 +93,7 @@ export default abstract class Reader { /** * Returns true if directory must be read. */ - public deep(entry: IEntry, negative: Pattern[]): boolean { + public deep(entry: IEntry, negative: Pattern[], globstar: boolean): boolean { if (!this.options.deep) { return false; } @@ -115,7 +115,11 @@ export default abstract class Reader { return false; } - return !micromatch.any(entry.path, negative, this.micromatchOptions); + if (micromatch.any(entry.path, negative, this.micromatchOptions)) { + return false; + } + + return globstar; } /** diff --git a/src/utils/pattern.spec.ts b/src/utils/pattern.spec.ts index eadc1a84..198d3dc0 100644 --- a/src/utils/pattern.spec.ts +++ b/src/utils/pattern.spec.ts @@ -98,4 +98,18 @@ describe('Utils → Pattern', () => { assert.equal(actual, expected); }); }); + + describe('.hasGlobStar', () => { + it('should returns true', () => { + const actual = util.hasGlobStar('**/*.js'); + + assert.ok(actual); + }); + + it('should returns false', () => { + const actual = util.hasGlobStar('*.js'); + + assert.ok(!actual); + }); + }); }); diff --git a/src/utils/pattern.ts b/src/utils/pattern.ts index e7b69875..5292e011 100644 --- a/src/utils/pattern.ts +++ b/src/utils/pattern.ts @@ -50,3 +50,10 @@ export function getPositivePatterns(patterns: Pattern[]): Pattern[] { export function getBaseDirectory(pattern: Pattern): string { return globParent(pattern); } + +/** + * Return true if provided pattern has globstar. + */ +export function hasGlobStar(pattern: Pattern): boolean { + return pattern.indexOf('**') !== -1; +}