diff --git a/src/index.spec.ts b/src/index.spec.ts index 4c9b026f..2e69c3a7 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -182,6 +182,17 @@ describe('Package', () => { assert.deepStrictEqual(actual, expected); }); + + it('should clean up patterns', () => { + const expected = [ + // Clean up duplicated slashes + tests.task.builder().base('fixtures').positive('fixtures/*').build() + ]; + + const actual = fg.generateTasks(['fixtures//*']); + + assert.deepStrictEqual(actual, expected); + }); }); describe('.isDynamicPattern', () => { diff --git a/src/index.ts b/src/index.ts index b6ef0c2d..9f957bd7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import * as taskManager from './managers/tasks'; +import * as patternManager from './managers/patterns'; import ProviderAsync from './providers/async'; import Provider from './providers/provider'; import ProviderStream from './providers/stream'; @@ -58,7 +59,7 @@ namespace FastGlob { export function generateTasks(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Task[] { assertPatternsInput(source); - const patterns = ([] as PatternInternal[]).concat(source); + const patterns = patternManager.transform(([] as PatternInternal[]).concat(source)); const settings = new Settings(options); return taskManager.generate(patterns, settings); @@ -80,7 +81,7 @@ namespace FastGlob { } function getWorks(source: PatternInternal | PatternInternal[], _Provider: new (settings: Settings) => Provider, options?: OptionsInternal): T[] { - const patterns = ([] as PatternInternal[]).concat(source); + const patterns = patternManager.transform(([] as PatternInternal[]).concat(source)); const settings = new Settings(options); const tasks = taskManager.generate(patterns, settings); diff --git a/src/managers/patterns.spec.ts b/src/managers/patterns.spec.ts new file mode 100644 index 00000000..bdac5a4e --- /dev/null +++ b/src/managers/patterns.spec.ts @@ -0,0 +1,57 @@ +import * as assert from 'assert'; + +import * as manager from './patterns'; + +describe('Managers → Pattern', () => { + describe('.transform', () => { + it('should do not change patterns', () => { + const expected = [ + 'directory/file.md', + 'files{.txt,/file.md}' + ]; + + const actual = manager.transform([ + 'directory/file.md', + 'files{.txt,/file.md}' + ]); + + assert.deepStrictEqual(actual, expected); + }); + + it('should do not change the device path in patterns with UNC parts', () => { + const expected = [ + '//?/D:/', + '//./D:/', + '//LOCALHOST/d$/', + '//127.0.0.1/d$/', + '//./UNC/LOCALHOST/d$/' + ]; + + const actual = manager.transform([ + '//?//D://', + '//.//D:///', + '//LOCALHOST//d$//', + '//127.0.0.1///d$//', + '//./UNC////LOCALHOST///d$//' + ]); + + assert.deepStrictEqual(actual, expected); + }); + + it('should remove duplicate slashes in the middle and the of the pattern', () => { + const expected = ['a/b', 'b/c', 'c/d/', '//?/D:/']; + + const actual = manager.transform(['a//b', 'b///c', 'c/d///', '//?//D://']); + + assert.deepStrictEqual(actual, expected); + }); + + it('should form double slashes at the beginning of the pattern', () => { + const expected = ['//*', '//?', '//?/D:/']; + + const actual = manager.transform(['///*', '////?', '///?/D:/']); + + assert.deepStrictEqual(actual, expected); + }); + }); +}); diff --git a/src/managers/patterns.ts b/src/managers/patterns.ts new file mode 100644 index 00000000..5a7a4378 --- /dev/null +++ b/src/managers/patterns.ts @@ -0,0 +1,17 @@ +/** + * Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string. + * The latter is due to the presence of the device path at the beginning of the UNC path. + */ +const DOUBLE_SLASH_RE = /(? removeDuplicatedSlashes(pattern)); +} + +/** + * This package only works with forward slashes as a path separator. + * Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes. + */ +function removeDuplicatedSlashes(pattern: string): string { + return pattern.replace(DOUBLE_SLASH_RE, '/'); +}