Skip to content

Commit 1d9d3ce

Browse files
authored
Merge pull request #392 from mrmlnc/ISSUE-240_p2p
ISSUE-240: Method to convert path to pattern
2 parents 4c60799 + 1fed462 commit 1d9d3ce

File tree

5 files changed

+298
-34
lines changed

5 files changed

+298
-34
lines changed

README.md

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ This package provides methods for traversing the file system and returning pathn
2525
* [Helpers](#helpers)
2626
* [generateTasks](#generatetaskspatterns-options)
2727
* [isDynamicPattern](#isdynamicpatternpattern-options)
28-
* [escapePath](#escapepathpattern)
28+
* [escapePath](#escapepathpath)
29+
* [convertPathToPattern](#convertpathtopatternpath)
2930
* [Options](#options-3)
3031
* [Common](#common)
3132
* [concurrency](#concurrency)
@@ -268,21 +269,57 @@ Any correct pattern.
268269

269270
See [Options](#options-3) section.
270271

271-
#### `escapePath(pattern)`
272+
#### `escapePath(path)`
272273

273-
Returns a path with escaped special characters (`*?|(){}[]`, `!` at the beginning of line, `@+!` before the opening parenthesis).
274+
Returns the path with escaped special characters depending on the platform.
275+
276+
* Posix:
277+
* `*?|(){}[]`;
278+
* `!` at the beginning of line;
279+
* `@+!` before the opening parenthesis;
280+
* `\\` before non-special characters;
281+
* Windows:
282+
* `(){}`
283+
* `!` at the beginning of line;
284+
* `@+!` before the opening parenthesis;
285+
* Characters like `*?|[]` cannot be used in the path ([windows_naming_conventions][windows_naming_conventions]), so they will not be escaped;
274286

275287
```js
276-
fg.escapePath('!abc'); // \\!abc
277-
fg.escapePath('C:/Program Files (x86)'); // C:/Program Files \\(x86\\)
288+
fg.escapePath('!abc');
289+
// \\!abc
290+
fg.escapePath('[OpenSource] mrmlnc – fast-glob (Deluxe Edition) 2014') + '/*.flac'
291+
// \\[OpenSource\\] mrmlnc – fast-glob \\(Deluxe Edition\\) 2014/*.flac
292+
293+
fg.posix.escapePath('C:\\Program Files (x86)\\**\\*');
294+
// C:\\\\Program Files \\(x86\\)\\*\\*\\*
295+
fg.win32.escapePath('C:\\Program Files (x86)\\**\\*');
296+
// Windows: C:\\Program Files \\(x86\\)\\**\\*
278297
```
279298

280-
##### pattern
299+
#### `convertPathToPattern(path)`
281300

282-
* Required: `true`
283-
* Type: `string`
301+
Converts a path to a pattern depending on the platform, including special character escaping.
302+
303+
* Posix. Works similarly to the `fg.posix.escapePath` method.
304+
* Windows. Works similarly to the `fg.win32.escapePath` method, additionally converting backslashes to forward slashes in cases where they are not escape characters (`!()+@{}`).
284305

285-
Any string, for example, a path to a file.
306+
```js
307+
fg.convertPathToPattern('[OpenSource] mrmlnc – fast-glob (Deluxe Edition) 2014') + '/*.flac';
308+
// \\[OpenSource\\] mrmlnc – fast-glob \\(Deluxe Edition\\) 2014/*.flac
309+
310+
fg.convertPathToPattern('C:/Program Files (x86)/**/*');
311+
// Posix: C:/Program Files \\(x86\\)/\\*\\*/\\*
312+
// Windows: C:/Program Files \\(x86\\)/**/*
313+
314+
fg.convertPathToPattern('C:\\Program Files (x86)\\**\\*');
315+
// Posix: C:\\\\Program Files \\(x86\\)\\*\\*\\*
316+
// Windows: C:/Program Files \\(x86\\)/**/*
317+
318+
fg.posix.convertPathToPattern('\\\\?\\c:\\Program Files (x86)') + '/**/*';
319+
// Posix: \\\\\\?\\\\c:\\\\Program Files \\(x86\\)/**/* (broken pattern)
320+
fg.win32.convertPathToPattern('\\\\?\\c:\\Program Files (x86)') + '/**/*';
321+
// Windows: //?/c:/Program Files \\(x86\\)/**/*
322+
```
286323

287324
## Options
288325

@@ -673,11 +710,11 @@ Always use forward-slashes in glob expressions (patterns and [`ignore`](#ignore)
673710
```ts
674711
[
675712
'directory/*',
676-
path.join(process.cwd(), '**').replace(/\\/g, '/')
713+
fg.convertPathToPattern(process.cwd()) + '/**'
677714
]
678715
```
679716

680-
> :book: Use the [`normalize-path`][npm_normalize_path] or the [`unixify`][npm_unixify] package to convert Windows-style path to a Unix-style path.
717+
> :book: Use the [`.convertPathToPattern`](#convertpathtopatternpath) package to convert Windows-style path to a Unix-style path.
681718
682719
Read more about [matching with backslashes][micromatch_backslashes].
683720

@@ -698,7 +735,7 @@ Refers to Bash. You need to escape special characters:
698735
fg.sync(['\\(special-*file\\).txt']) // ['(special-*file).txt']
699736
```
700737

701-
Read more about [matching special characters as literals][picomatch_matching_special_characters_as_literals].
738+
Read more about [matching special characters as literals][picomatch_matching_special_characters_as_literals]. Or use the [`.escapePath`](#escapepathpath).
702739

703740
## How to exclude directory from reading?
704741

@@ -724,11 +761,15 @@ You have to understand that if you write the pattern to exclude directories, the
724761

725762
## How to use UNC path?
726763

727-
You cannot use [Uniform Naming Convention (UNC)][unc_path] paths as patterns (due to syntax), but you can use them as [`cwd`](#cwd) directory.
764+
You cannot use [Uniform Naming Convention (UNC)][unc_path] paths as patterns (due to syntax) directly, but you can use them as [`cwd`](#cwd) directory or use the `fg.convertPathToPattern` method.
728765

729766
```ts
767+
// cwd
730768
fg.sync('*', { cwd: '\\\\?\\C:\\Python27' /* or //?/C:/Python27 */ });
731769
fg.sync('Python27/*', { cwd: '\\\\?\\C:\\' /* or //?/C:/ */ });
770+
771+
// .convertPathToPattern
772+
fg.sync(fg.convertPathToPattern('\\\\?\\c:\\Python27') + '/*');
732773
```
733774

734775
## Compatible with `node-glob`?
@@ -815,3 +856,4 @@ This software is released under the terms of the MIT license.
815856
[zotac_bi323]: https://www.zotac.com/ee/product/mini_pcs/zbox-bi323
816857
[nodejs_thread_pool]: https://nodejs.org/en/docs/guides/dont-block-the-event-loop
817858
[libuv_thread_pool]: http://docs.libuv.org/en/v1.x/threadpool.html
859+
[windows_naming_conventions]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions

src/index.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,61 @@ describe('Package', () => {
238238
assert.strictEqual(actual, expected);
239239
});
240240
});
241+
242+
describe('.convertPathToPattern', () => {
243+
it('should return a pattern', () => {
244+
// In posix system \\ is a escaping character and it will be escaped before non-special characters.
245+
const posix = 'C:\\\\Program Files \\(x86\\)\\*\\*\\*';
246+
const windows = 'C:/Program Files \\(x86\\)/**/*';
247+
const expected = tests.platform.isWindows() ? windows : posix;
248+
249+
const actual = fg.convertPathToPattern('C:\\Program Files (x86)\\**\\*');
250+
251+
assert.strictEqual(actual, expected);
252+
});
253+
});
254+
255+
describe('posix', () => {
256+
describe('.escapePath', () => {
257+
it('should return escaped path', () => {
258+
const expected = '/directory/\\*\\*/\\*';
259+
260+
const actual = fg.posix.escapePath('/directory/*\\*/*');
261+
262+
assert.strictEqual(actual, expected);
263+
});
264+
});
265+
266+
describe('.convertPathToPattern', () => {
267+
it('should return a pattern', () => {
268+
const expected = 'a\\*.txt';
269+
270+
const actual = fg.posix.convertPathToPattern('a\\*.txt');
271+
272+
assert.strictEqual(actual, expected);
273+
});
274+
});
275+
});
276+
277+
describe('win32', () => {
278+
describe('.escapePath', () => {
279+
it('should return escaped path', () => {
280+
const expected = 'C:\\Program Files \\(x86\\)\\**\\*';
281+
282+
const actual = fg.win32.escapePath('C:\\Program Files (x86)\\**\\*');
283+
284+
assert.strictEqual(actual, expected);
285+
});
286+
});
287+
288+
describe('.convertPathToPattern', () => {
289+
it('should return a pattern', () => {
290+
const expected = 'C:/Program Files \\(x86\\)/**/*';
291+
292+
const actual = fg.win32.convertPathToPattern('C:\\Program Files (x86)\\**\\*');
293+
294+
assert.strictEqual(actual, expected);
295+
});
296+
});
297+
});
241298
});

src/index.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,45 @@ namespace FastGlob {
7878
return utils.pattern.isDynamicPattern(source, settings);
7979
}
8080

81-
export function escapePath(source: PatternInternal): PatternInternal {
81+
export function escapePath(source: string): PatternInternal {
8282
assertPatternsInput(source);
8383

8484
return utils.path.escape(source);
8585
}
86+
87+
export function convertPathToPattern(source: string): PatternInternal {
88+
assertPatternsInput(source);
89+
90+
return utils.path.convertPathToPattern(source);
91+
}
92+
93+
export namespace posix {
94+
export function escapePath(source: string): PatternInternal {
95+
assertPatternsInput(source);
96+
97+
return utils.path.escapePosixPath(source);
98+
}
99+
100+
export function convertPathToPattern(source: string): PatternInternal {
101+
assertPatternsInput(source);
102+
103+
return utils.path.convertPosixPathToPattern(source);
104+
}
105+
}
106+
107+
export namespace win32 {
108+
export function escapePath(source: string): PatternInternal {
109+
assertPatternsInput(source);
110+
111+
return utils.path.escapeWindowsPath(source);
112+
}
113+
114+
export function convertPathToPattern(source: string): PatternInternal {
115+
assertPatternsInput(source);
116+
117+
return utils.path.convertWindowsPathToPattern(source);
118+
}
119+
}
86120
}
87121

88122
function getWorks<T>(source: PatternInternal | PatternInternal[], _Provider: new (settings: Settings) => Provider<T>, options?: OptionsInternal): T[] {

src/utils/path.spec.ts

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,7 @@ describe('Utils → Path', () => {
2424
});
2525
});
2626

27-
describe('.escapePattern', () => {
28-
it('should return pattern with escaped glob symbols', () => {
29-
assert.strictEqual(util.escape('!abc'), '\\!abc');
30-
assert.strictEqual(util.escape('*'), '\\*');
31-
assert.strictEqual(util.escape('?'), '\\?');
32-
assert.strictEqual(util.escape('()'), '\\(\\)');
33-
assert.strictEqual(util.escape('{}'), '\\{\\}');
34-
assert.strictEqual(util.escape('[]'), '\\[\\]');
35-
assert.strictEqual(util.escape('@('), '\\@\\(');
36-
assert.strictEqual(util.escape('!('), '\\!\\(');
37-
assert.strictEqual(util.escape('*('), '\\*\\(');
38-
assert.strictEqual(util.escape('?('), '\\?\\(');
39-
assert.strictEqual(util.escape('+('), '\\+\\(');
40-
});
41-
27+
describe('.escape', () => {
4228
it('should return pattern without additional escape characters', () => {
4329
assert.strictEqual(util.escape('\\!abc'), '\\!abc');
4430
assert.strictEqual(util.escape('\\*'), '\\*');
@@ -55,6 +41,34 @@ describe('Utils → Path', () => {
5541
});
5642
});
5743

44+
describe('.escapePosixPattern', () => {
45+
it('should return pattern with escaped glob symbols', () => {
46+
assert.strictEqual(util.escapePosixPath('!abc'), '\\!abc');
47+
assert.strictEqual(util.escapePosixPath('*'), '\\*');
48+
assert.strictEqual(util.escapePosixPath('?'), '\\?');
49+
assert.strictEqual(util.escapePosixPath('\\'), '\\\\');
50+
assert.strictEqual(util.escapePosixPath('()'), '\\(\\)');
51+
assert.strictEqual(util.escapePosixPath('{}'), '\\{\\}');
52+
assert.strictEqual(util.escapePosixPath('[]'), '\\[\\]');
53+
assert.strictEqual(util.escapePosixPath('@('), '\\@\\(');
54+
assert.strictEqual(util.escapePosixPath('!('), '\\!\\(');
55+
assert.strictEqual(util.escapePosixPath('*('), '\\*\\(');
56+
assert.strictEqual(util.escapePosixPath('?('), '\\?\\(');
57+
assert.strictEqual(util.escapePosixPath('+('), '\\+\\(');
58+
});
59+
});
60+
61+
describe('.escapeWindowsPattern', () => {
62+
it('should return pattern with escaped glob symbols', () => {
63+
assert.strictEqual(util.escapeWindowsPath('!abc'), '\\!abc');
64+
assert.strictEqual(util.escapeWindowsPath('()'), '\\(\\)');
65+
assert.strictEqual(util.escapeWindowsPath('{}'), '\\{\\}');
66+
assert.strictEqual(util.escapeWindowsPath('@('), '\\@\\(');
67+
assert.strictEqual(util.escapeWindowsPath('!('), '\\!\\(');
68+
assert.strictEqual(util.escapeWindowsPath('+('), '\\+\\(');
69+
});
70+
});
71+
5872
describe('.removeLeadingDotCharacters', () => {
5973
it('should return path without changes', () => {
6074
assert.strictEqual(util.removeLeadingDotSegment('../a/b'), '../a/b');
@@ -73,4 +87,84 @@ describe('Utils → Path', () => {
7387
assert.strictEqual(util.removeLeadingDotSegment('.\\a\\b'), 'a\\b');
7488
});
7589
});
90+
91+
describe('.convertPathToPattern', () => {
92+
it('should return a pattern', () => {
93+
assert.strictEqual(util.convertPathToPattern('.{directory}'), '.\\{directory\\}');
94+
});
95+
});
96+
97+
describe('.convertPosixPathToPattern', () => {
98+
it('should escape special characters', () => {
99+
assert.strictEqual(util.convertPosixPathToPattern('./**\\*'), './\\*\\*\\*');
100+
});
101+
});
102+
103+
describe('.convertWindowsPathToPattern', () => {
104+
it('should escape special characters', () => {
105+
assert.strictEqual(util.convertPosixPathToPattern('.{directory}'), '.\\{directory\\}');
106+
});
107+
108+
it('should do nothing with escaped glob symbols', () => {
109+
assert.strictEqual(util.convertWindowsPathToPattern('\\!\\'), '\\!/');
110+
assert.strictEqual(util.convertWindowsPathToPattern('\\+\\'), '\\+/');
111+
assert.strictEqual(util.convertWindowsPathToPattern('\\@\\'), '\\@/');
112+
assert.strictEqual(util.convertWindowsPathToPattern('\\(\\'), '\\(/');
113+
assert.strictEqual(util.convertWindowsPathToPattern('\\)\\'), '\\)/');
114+
assert.strictEqual(util.convertWindowsPathToPattern('\\{\\'), '\\{/');
115+
assert.strictEqual(util.convertWindowsPathToPattern('\\}\\'), '\\}/');
116+
117+
assert.strictEqual(util.convertWindowsPathToPattern('.\\*'), './*');
118+
assert.strictEqual(util.convertWindowsPathToPattern('.\\**'), './**');
119+
assert.strictEqual(util.convertWindowsPathToPattern('.\\**\\*'), './**/*');
120+
121+
assert.strictEqual(util.convertWindowsPathToPattern('a\\{b,c\\d,{b,c}}'), 'a\\{b,c/d,\\{b,c\\}\\}');
122+
});
123+
124+
it('should convert slashes', () => {
125+
assert.strictEqual(util.convertWindowsPathToPattern('/'), '/');
126+
assert.strictEqual(util.convertWindowsPathToPattern('\\'), '/');
127+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\'), '//');
128+
assert.strictEqual(util.convertWindowsPathToPattern('\\/'), '//');
129+
assert.strictEqual(util.convertWindowsPathToPattern('\\/\\'), '///');
130+
});
131+
132+
it('should convert relative paths', () => {
133+
assert.strictEqual(util.convertWindowsPathToPattern('file.txt'), 'file.txt');
134+
assert.strictEqual(util.convertWindowsPathToPattern('./file.txt'), './file.txt');
135+
assert.strictEqual(util.convertWindowsPathToPattern('.\\file.txt'), './file.txt');
136+
assert.strictEqual(util.convertWindowsPathToPattern('../file.txt'), '../file.txt');
137+
assert.strictEqual(util.convertWindowsPathToPattern('..\\file.txt'), '../file.txt');
138+
assert.strictEqual(util.convertWindowsPathToPattern('.\\file.txt'), './file.txt');
139+
});
140+
141+
it('should convert absolute paths', () => {
142+
assert.strictEqual(util.convertWindowsPathToPattern('/.file.txt'), '/.file.txt');
143+
assert.strictEqual(util.convertWindowsPathToPattern('/root/.file.txt'), '/root/.file.txt');
144+
assert.strictEqual(util.convertWindowsPathToPattern('\\.file.txt'), '/.file.txt');
145+
assert.strictEqual(util.convertWindowsPathToPattern('\\root\\.file.txt'), '/root/.file.txt');
146+
assert.strictEqual(util.convertWindowsPathToPattern('\\root/.file.txt'), '/root/.file.txt');
147+
});
148+
149+
it('should convert traditional DOS paths', () => {
150+
assert.strictEqual(util.convertWindowsPathToPattern('D:ShipId.txt'), 'D:ShipId.txt');
151+
assert.strictEqual(util.convertWindowsPathToPattern('D:/ShipId.txt'), 'D:/ShipId.txt');
152+
assert.strictEqual(util.convertWindowsPathToPattern('D://ShipId.txt'), 'D://ShipId.txt');
153+
154+
assert.strictEqual(util.convertWindowsPathToPattern('D:\\ShipId.txt'), 'D:/ShipId.txt');
155+
assert.strictEqual(util.convertWindowsPathToPattern('D:\\\\ShipId.txt'), 'D://ShipId.txt');
156+
assert.strictEqual(util.convertWindowsPathToPattern('D:\\/ShipId.txt'), 'D://ShipId.txt');
157+
});
158+
159+
it('should convert UNC paths', () => {
160+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\system07\\'), '//system07/');
161+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\system07\\c$\\'), '//system07/c$/');
162+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\Server02\\Share\\Foo.txt'), '//Server02/Share/Foo.txt');
163+
164+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\127.0.0.1\\c$\\File.txt'), '//127.0.0.1/c$/File.txt');
165+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\.\\c:\\File.txt'), '//./c:/File.txt');
166+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\?\\c:\\File.txt'), '//?/c:/File.txt');
167+
assert.strictEqual(util.convertWindowsPathToPattern('\\\\.\\UNC\\LOCALHOST\\c$\\File.txt'), '//./UNC/LOCALHOST/c$/File.txt');
168+
});
169+
});
76170
});

0 commit comments

Comments
 (0)