Skip to content

Commit

Permalink
Handle UNC paths, add preserveMultipleSlashes
Browse files Browse the repository at this point in the history
This adds some slightly modified behavior when a pattern or path starts
with `//` on Windows.

- In the case of `//?/<drive letter>:/...`, the `?` is treated as a
  literal character, rather than a wildcard. That is, `//?/c:` will
  _not_ match `//x/c:`.
- UNC patterns starting with `//?/<drive letter>:/` will match file
  paths starting with `<drive letter>:` if the drive letters match
  case-insensitively.
- File paths starting with `//?/<drive letter>:/` will match file
  patterns starting with `<drive letter>:` if the drive letters match
  case-insensitively.

Add `{preserveMultipleSlashes:true}` option to suppress the
behavior where multiple consecutive `/` characters would be
effectively coerced into a single path portion separator.
  • Loading branch information
isaacs committed Jan 17, 2023
1 parent 0f47274 commit a5f6a25
Show file tree
Hide file tree
Showing 15 changed files with 471 additions and 265 deletions.
151 changes: 92 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,114 +13,137 @@ objects.
// hybrid module, load with require() or import
import { minimatch } from 'minimatch'
// or:
const { minimatch } = require("minimatch")
const { minimatch } = require('minimatch')

minimatch("bar.foo", "*.foo") // true!
minimatch("bar.foo", "*.bar") // false!
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
minimatch('bar.foo', '*.foo') // true!
minimatch('bar.foo', '*.bar') // false!
minimatch('bar.foo', '*.+(bar|foo)', { debug: true }) // true, and noisy!
```

## Features

Supports these glob features:

* Brace Expansion
* Extended glob matching
* "Globstar" `**` matching
- Brace Expansion
- Extended glob matching
- "Globstar" `**` matching

See:

* `man sh`
* `man bash`
* `man 3 fnmatch`
* `man 5 gitignore`
- `man sh`
- `man bash`
- `man 3 fnmatch`
- `man 5 gitignore`

## Windows

**Please only use forward-slashes in glob expressions.**

Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes in patterns
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes in patterns
will always be interpreted as escape characters, not path separators.

Note that `\` or `/` _will_ be interpreted as path separators in paths on
Windows, and will match against `/` in glob expressions.

So just always use `/` in patterns.

### UNC Paths

On Windows, UNC paths like `//?/c:/...` or
`//ComputerName/Share/...` are handled specially.

- Patterns starting with a double-slash followed by some
non-slash characters will preserve their double-slash. As a
result, a pattern like `//*` will match `//x`, but not `/x`.
- Patterns staring with `//?/<drive letter>:` will *not* treat
the `?` as a wildcard character. Instead, it will be treated
as a normal string.
- Patterns starting with `//?/<drive letter>:/...` will match
file paths starting with `<drive letter>:/...`, and vice versa,
as if the `//?/` was not present. This behavior only is
present when the drive letters are a case-insensitive match to
one another. The remaining portions of the path/pattern are
compared case sensitively, unless `nocase:true` is set.

Note that specifying a UNC path using `\` characters as path
separators is always allowed in the file path argument, but only
allowed in the pattern argument when `windowsPathsNoEscape: true`
is set in the options.

## Minimatch Class

Create a minimatch object by instantiating the `minimatch.Minimatch` class.

```javascript
var Minimatch = require("minimatch").Minimatch
var Minimatch = require('minimatch').Minimatch
var mm = new Minimatch(pattern, options)
```

### Properties

* `pattern` The original pattern the minimatch object represents.
* `options` The options supplied to the constructor.
* `set` A 2-dimensional array of regexp or string expressions.
- `pattern` The original pattern the minimatch object represents.
- `options` The options supplied to the constructor.
- `set` A 2-dimensional array of regexp or string expressions.
Each row in the
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
`{a,b/c}/d` would expand to a set of patterns like:

[ [ a, d ]
, [ b, c, d ] ]

If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.
If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.

* `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
- `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
* `negate` True if the pattern is negated.
* `comment` True if the pattern is a comment.
* `empty` True if the pattern is `""`.
- `negate` True if the pattern is negated.
- `comment` True if the pattern is a comment.
- `empty` True if the pattern is `""`.

### Methods

* `makeRe` Generate the `regexp` member if necessary, and return it.
- `makeRe` Generate the `regexp` member if necessary, and return it.
Will return `false` if the pattern is invalid.
* `match(fname)` Return true if the filename matches the pattern, or
- `match(fname)` Return true if the filename matches the pattern, or
false otherwise.
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
- `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
method is mainly for internal use, but is exposed so that it can be
used by a glob-walker that needs to avoid excessive filesystem calls.

All other methods are internal, and will be called as necessary.

### minimatch(path, pattern, options)

Main export. Tests a path against the pattern using the options.
Main export. Tests a path against the pattern using the options.

```javascript
var isJS = minimatch(file, "*.js", { matchBase: true })
var isJS = minimatch(file, '*.js', { matchBase: true })
```

### minimatch.filter(pattern, options)

Returns a function that tests its
supplied argument, suitable for use with `Array.filter`. Example:
supplied argument, suitable for use with `Array.filter`. Example:

```javascript
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
var javascripts = fileList.filter(minimatch.filter('*.js', { matchBase: true }))
```

### minimatch.match(list, pattern, options)

Match against the list of
files, in the style of fnmatch or glob. If nothing is matched, and
files, in the style of fnmatch or glob. If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.

```javascript
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true})
var javascripts = minimatch.match(fileList, '*.js', { matchBase: true })
```

### minimatch.makeRe(pattern, options)
Expand Down Expand Up @@ -162,13 +185,13 @@ Perform a case-insensitive match.
### nonull

When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set. When not set, an empty list
the pattern itself if this option is set. When not set, an empty list
is returned if there are no matches.

### matchBase

If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes. For example,
against the basename of the path if it contains slashes. For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.

### nocomment
Expand All @@ -187,73 +210,83 @@ Returns from negate expressions the same as if they were not negated.

### partial

Compare a partial path to a pattern. As long as the parts of the path that
Compare a partial path to a pattern. As long as the parts of the path that
are present are not contradicted by the pattern, it will be treated as a
match. This is useful in applications where you're walking through a
match. This is useful in applications where you're walking through a
folder structure, and don't yet have the full path, but want to ensure that
you do not walk down paths that can never be a match.

For example,

```js
minimatch('/a/b', '/a/*/c/d', { partial: true }) // true, might be /a/b/c/d
minimatch('/a/b', '/**/d', { partial: true }) // true, might be /a/b/.../d
minimatch('/a/b', '/a/*/c/d', { partial: true }) // true, might be /a/b/c/d
minimatch('/a/b', '/**/d', { partial: true }) // true, might be /a/b/.../d
minimatch('/x/y/z', '/a/**/z', { partial: true }) // false, because x !== a
```

### windowsPathsNoEscape

Use `\\` as a path separator _only_, and _never_ as an escape
character. If set, all `\\` characters are replaced with `/` in
the pattern. Note that this makes it **impossible** to match
character. If set, all `\\` characters are replaced with `/` in
the pattern. Note that this makes it **impossible** to match
against paths containing literal glob pattern characters, but
allows matching with patterns constructed using `path.join()` and
`path.resolve()` on Windows platforms, mimicking the (buggy!)
behavior of earlier versions on Windows. Please use with
behavior of earlier versions on Windows. Please use with
caution, and be mindful of [the caveat about Windows
paths](#windows).

For legacy reasons, this is also set if
`options.allowWindowsEscape` is set to the exact value `false`.

### preserveMultipleSlashes

By default, multiple `/` characters (other than the leading `//`
in a UNC path, see "UNC Paths" above) are treated as a single
`/`.

That is, a pattern like `a///b` will match the file path `a/b`.

Set `preserveMultipleSlashes: true` to suppress this behavior.

## Comparisons to other fnmatch/glob implementations

While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between minimatch and other
implementations, and are intentional.

If the pattern starts with a `!` character, then it is negated. Set the
If the pattern starts with a `!` character, then it is negated. Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.

If a pattern starts with `#`, then it is treated as a comment, and
will not match anything. Use `\#` to match a literal `#` at the
will not match anything. Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.

The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.

If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes. For example,
interpreting the character escapes. For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.

If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
checked for validity. Since those two are valid, matching proceeds.

Note that `fnmatch(3)` in libc is an extremely naive string comparison
matcher, which does not do anything special for slashes. This library is
matcher, which does not do anything special for slashes. This library is
designed to be used in glob searching and file walkers, and so it does do
special things with `/`. Thus, `foo*` will not match `foo/bar` in this
special things with `/`. Thus, `foo*` will not match `foo/bar` in this
library, even though it would in `fnmatch(3)`.
27 changes: 24 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# change log

## 6.1

- Handle UNC paths on Windows

This adds some slightly modified behavior when a pattern or path starts
with `//` on Windows.

- In the case of `//?/<drive letter>:/...`, the `?` is treated as a
literal character, rather than a wildcard. That is, `//?/c:` will
_not_ match `//x/c:`.
- UNC patterns starting with `//?/<drive letter>:/` will match file paths
starting with `<drive letter>:` if the drive letters match
case-insensitively.
- File paths starting with `//?/<drive letter>:/` will match file
patterns starting with `<drive letter>:` if the drive letters match
case-insensitively.

- Add `{preserveMultipleSlashes:true}` option to suppress the
behavior where multiple consecutive `/` characters would be
effectively coerced into a single path portion separator.

## 6.0

- hybrid module supporting both `require()` and `import`
Expand All @@ -9,9 +30,9 @@

- use windowsPathNoEscape/allowWindowsEscape opts
- make character classes more faithful to bash glob behavior
- fix handling of escapes
- treat invalid character classes as non-matching pattern
rather than escaped literals
- fix handling of escapes
- treat invalid character classes as non-matching pattern
rather than escaped literals

## 5.0

Expand Down
Loading

0 comments on commit a5f6a25

Please sign in to comment.