Skip to content
Open
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
33 changes: 31 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,33 @@ const cleanRangeBackSlash = slashes => {
return slashes.slice(0, length - length % 2)
}

// POSIX character classes ('[:alpha:]', '[:digit:]', ...) are part of the
// wildmatch syntax gitignore uses, but JavaScript regular expressions have no
// equivalent, so expand each one to its range (matching Git in the C locale).
const POSIX_CLASS = {
alnum: '0-9A-Za-z',
alpha: 'A-Za-z',
blank: ' \\t',
cntrl: '\\x00-\\x1f\\x7f',
digit: '0-9',
graph: '\\x21-\\x7e',
lower: 'a-z',
print: '\\x20-\\x7e',
punct: '\\x21-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7e',
space: ' \\t\\n\\x0b\\f\\r',
upper: 'A-Z',
xdigit: '0-9A-Fa-f'
}

const REGEX_POSIX_CLASS = /\[:([a-z]+):\]/g

const expandPosixClasses = range => range.replace(
REGEX_POSIX_CLASS,
(match, name) => Object.prototype.hasOwnProperty.call(POSIX_CLASS, name)
? POSIX_CLASS[name]
: match
)

// > If the pattern ends with a slash,
// > it is removed for the purpose of the following description,
// > but it would only find a match with a directory.
Expand Down Expand Up @@ -272,7 +299,9 @@ const REPLACERS = [
// > can be used to match one of the characters in a range.

// `\` is escaped by step 3
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
// A POSIX class '[:name:]' is consumed as a unit so its inner ']' is not
// mistaken for the end of the bracket expression.
/(\\)?\[((?:\[:[a-z]+:\]|[^\]/])*?)(\\*)($|\])/g,
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE
// '\\[bar]' -> '\\\\[bar\\]'
? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}`
Expand All @@ -281,7 +310,7 @@ const REPLACERS = [
// A normal case, and it is a range notation
// '[bar]'
// '[bar\\\\]'
? `[${negateRange(sanitizeRange(range))}${endEscape}]`
? `[${expandPosixClasses(negateRange(sanitizeRange(range)))}${endEscape}]`
// Invalid range notaton
// '[bar\\]' -> '[bar\\\\]'
: '[]'
Expand Down
52 changes: 52 additions & 0 deletions test/fixtures/cases.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,58 @@ const cases = [
'a.9': 0
}
],
[
'POSIX class: [[:digit:]]',
[
'[[:digit:]].log'
],
{
'1.log': 1,
'a.log': 0
}
],
[
'POSIX class: [[:alpha:]]',
[
'[[:alpha:]].log'
],
{
'a.log': 1,
'1.log': 0
}
],
[
'POSIX class: negated [![:digit:]]',
[
'x[![:digit:]]'
],
{
'xa': 1,
'x1': 0
}
],
[
'POSIX class combined with a set: [[:alnum:]_]',
[
'[[:alnum:]_].o'
],
{
'a.o': 1,
'_.o': 1,
'!.o': 0
}
],
[
// An unknown class name is left as-is, matching Git (no match).
'POSIX class: unknown name is inert',
[
'[[:foo:]]'
],
{
'foo': 0,
'f': 0
}
],
[
// Just treat it as normal character set
'special case: range-like character set',
Expand Down