diff --git a/README.md b/README.md index 184ceac4..b8b51604 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,20 @@ Supports these glob features: - Brace Expansion - Extended glob matching - "Globstar" `**` matching +- [Posix character + classes](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html), + like `[[:alpha:]]`, supporting the full range of Unicode + characters. For example, `[[:alpha:]]` will match against + `'é'`, though `[a-zA-Z]` will not. Collating symbol and set + matching is not supported, so `[[=e=]]` will _not_ match `'é'` + and `[[.ch.]]` will not match `'ch'` in locales where `ch` is + considered a single character. See: - `man sh` -- `man bash` +- `man bash` [Pattern + Matching](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html) - `man 3 fnmatch` - `man 5 gitignore` diff --git a/changelog.md b/changelog.md index b7048bc2..2b13c9f0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # change log +## 7.3 + +- Add support for posix character classes in a unicode-aware way. + ## 7.2 - Add `windowsNoMagicRoot` option diff --git a/src/brace-expressions.ts b/src/brace-expressions.ts new file mode 100644 index 00000000..0476f83e --- /dev/null +++ b/src/brace-expressions.ts @@ -0,0 +1,153 @@ +// translate the various posix character classes into unicode properties +// this works across all unicode locales + +// { : [, /u flag required, negated] +const posixClasses: { [k: string]: [e: string, u: boolean, n?: boolean] } = { + '[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true], + '[:alpha:]': ['\\p{L}\\p{Nl}', true], + '[:ascii:]': ['\\x' + '00-\\x' + '7f', false], + '[:blank:]': ['\\p{Zs}\\t', true], + '[:cntrl:]': ['\\p{Cc}', true], + '[:digit:]': ['\\p{Nd}', true], + '[:graph:]': ['\\p{Z}\\p{C}', true, true], + '[:lower:]': ['\\p{Ll}', true], + '[:print:]': ['\\p{C}', true], + '[:punct:]': ['\\p{P}', true], + '[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true], + '[:upper:]': ['\\p{Lu}', true], + '[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true], + '[:xdigit:]': ['A-Fa-f0-9', false], +} + +// only need to escape a few things inside of brace expressions +const regExpEscape = (s: string) => s.replace(/[[\]\\-]/g, '\\$&') + +const rangesToString = (ranges: string[]): string => { + return ( + ranges + // .map(r => r.replace(/[[\]]/g, '\\$&').replace(/^-/, '\\-')) + .join('') + ) +} + +// takes a glob string at a posix brace expression, and returns +// an equivalent regular expression source, and boolean indicating +// whether the /u flag needs to be applied, and the number of chars +// consumed to parse the character class. +// This also removes out of order ranges, and returns ($.) if the +// entire class just no good. +export const parseClass = ( + glob: string, + position: number +): [string, boolean, number] => { + const pos = position + /* c8 ignore start */ + if (glob.charAt(pos) !== '[') { + throw new Error('not in a brace expression') + } + /* c8 ignore stop */ + const ranges: string[] = [] + const negs: string[] = [] + + let i = pos + 1 + let sawStart = false + let uflag = false + let escaping = false + let negate = false + let endPos = pos + let rangeStart = '' + WHILE: while (i < glob.length) { + const c = glob.charAt(i) + if ((c === '!' || c === '^') && i === pos + 1) { + negate = true + i++ + continue + } + + if (c === ']' && sawStart && !escaping) { + endPos = i + 1 + break + } + + sawStart = true + if (c === '\\') { + if (!escaping) { + escaping = true + i++ + continue + } + // escaped \ char, fall through and treat like normal char + } + if (c === '[' && !escaping) { + // either a posix class, a collation equivalent, or just a [ + for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) { + if (glob.startsWith(cls, i)) { + // invalid, [a-[] is fine, but not [a-[:alpha]] + if (rangeStart) { + return ['$.', false, glob.length - pos] + } + i += cls.length + if (neg) negs.push(unip) + else ranges.push(unip) + uflag = uflag || u + continue WHILE + } + } + } + + // now it's just a normal character, effectively + escaping = false + if (rangeStart) { + // throw this range away if it's not valid, but others + // can still match. + if (c > rangeStart) { + ranges.push(regExpEscape(rangeStart) + '-' + regExpEscape(c)) + } else if (c === rangeStart) { + ranges.push(regExpEscape(c)) + } + rangeStart = '' + i++ + continue + } + + // now might be the start of a range. + // can be either c-d or c-] or c] or c] at this point + if (glob.startsWith('-]', i + 1)) { + ranges.push(regExpEscape(c + '-')) + i += 2 + continue + } + if (glob.startsWith('-', i + 1)) { + rangeStart = c + i += 2 + continue + } + + // not the start of a range, just a single character + ranges.push(regExpEscape(c)) + i++ + } + + if (endPos < i) { + // didn't see the end of the class, not a valid class, + // but might still be valid as a literal match. + return ['', false, 0] + } + + // if we got no ranges and no negates, then we have a range that + // cannot possibly match anything, and that poisons the whole glob + if (!ranges.length && !negs.length) { + return ['$.', false, glob.length - pos] + } + + const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']' + const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']' + const comb = + ranges.length && negs.length + ? '(' + sranges + '|' + snegs + ')' + : ranges.length + ? sranges + : snegs + + return [comb, uflag, endPos - pos] +} diff --git a/src/index.ts b/src/index.ts index b14c22c7..1b2cc2cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,6 @@ +import expand from 'brace-expansion' +import { parseClass } from './brace-expressions.js' + export interface MinimatchOptions { nobrace?: boolean nocomment?: boolean @@ -111,7 +114,6 @@ minimatch.sep = sep export const GLOBSTAR = Symbol('globstar **') minimatch.GLOBSTAR = GLOBSTAR -import expand from 'brace-expansion' const plTypes = { '!': { open: '(?:(?!(?:', close: '))[^/]*?)' }, @@ -251,7 +253,6 @@ const assertValidPattern: (pattern: any) => void = ( // when it is the *only* thing in a path portion. Otherwise, any series // of * is equivalent to a single *. Globstar behavior is enabled by // default, and can be disabled by setting options.noglobstar. -const SUBPARSE = Symbol('subparse') export const makeRe = (pattern: string, options: MinimatchOptions = {}) => new Minimatch(pattern, options).makeRe() @@ -274,10 +275,8 @@ minimatch.match = match // replace stuff like \* with * const globUnescape = (s: string) => s.replace(/\\(.)/g, '$1') const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/ -const charUnescape = (s: string) => s.replace(/\\([^-\]])/g, '$1') const regExpEscape = (s: string) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') -const braExpEscape = (s: string) => s.replace(/[[\]\\]/g, '\\$&') interface PatternListEntry { type: string @@ -965,10 +964,7 @@ export class Minimatch { return braceExpand(this.pattern, this.options) } - parse( - pattern: string, - isSub?: typeof SUBPARSE - ): ParseReturn | SubparseReturn { + parse(pattern: string): ParseReturn | SubparseReturn { assertValidPattern(pattern) const options = this.options @@ -981,34 +977,32 @@ export class Minimatch { // *, *.*, and *. Add a fast check method for those. let m: RegExpMatchArray | null let fastTest: null | ((f: string) => boolean) = null - if (isSub !== SUBPARSE) { - if ((m = pattern.match(starRE))) { - fastTest = options.dot ? starTestDot : starTest - } else if ((m = pattern.match(starDotExtRE))) { - fastTest = ( - options.nocase - ? options.dot - ? starDotExtTestNocaseDot - : starDotExtTestNocase - : options.dot - ? starDotExtTestDot - : starDotExtTest - )(m[1]) - } else if ((m = pattern.match(qmarksRE))) { - fastTest = ( - options.nocase - ? options.dot - ? qmarksTestNocaseDot - : qmarksTestNocase - : options.dot - ? qmarksTestDot - : qmarksTest - )(m) - } else if ((m = pattern.match(starDotStarRE))) { - fastTest = options.dot ? starDotStarTestDot : starDotStarTest - } else if ((m = pattern.match(dotStarRE))) { - fastTest = dotStarTest - } + if ((m = pattern.match(starRE))) { + fastTest = options.dot ? starTestDot : starTest + } else if ((m = pattern.match(starDotExtRE))) { + fastTest = ( + options.nocase + ? options.dot + ? starDotExtTestNocaseDot + : starDotExtTestNocase + : options.dot + ? starDotExtTestDot + : starDotExtTest + )(m[1]) + } else if ((m = pattern.match(qmarksRE))) { + fastTest = ( + options.nocase + ? options.dot + ? qmarksTestNocaseDot + : qmarksTestNocase + : options.dot + ? qmarksTestDot + : qmarksTest + )(m) + } else if ((m = pattern.match(starDotStarRE))) { + fastTest = options.dot ? starDotStarTestDot : starDotStarTest + } else if ((m = pattern.match(dotStarRE))) { + fastTest = dotStarTest } let re = '' @@ -1018,12 +1012,8 @@ export class Minimatch { const patternListStack: PatternListEntry[] = [] const negativeLists: NegativePatternListEntry[] = [] let stateChar: StateChar | false = false - let inClass = false - let reClassStart = -1 - let classStart = -1 - let cs: string + let uflag = false let pl: PatternListEntry | undefined - let sp: SubparseReturn // . and .. never match anything that doesn't start with ., // even when options.dot is set. However, if the pattern // starts with ., then traversal patterns can match. @@ -1098,11 +1088,6 @@ export class Minimatch { /* c8 ignore stop */ case '\\': - if (inClass && pattern.charAt(i + 1) === '-') { - re += c - continue - } - clearStateChar() escaping = true continue @@ -1116,15 +1101,6 @@ export class Minimatch { case '!': this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === '!' && i === classStart + 1) c = '^' - re += c - continue - } - // if we already have a stateChar, then it means // that there was something like ** or +? in there. // Handle the stateChar, then proceed with this one. @@ -1138,11 +1114,6 @@ export class Minimatch { continue case '(': { - if (inClass) { - re += '(' - continue - } - if (!stateChar) { re += '\\(' continue @@ -1171,7 +1142,7 @@ export class Minimatch { case ')': { const plEntry = patternListStack[patternListStack.length - 1] - if (inClass || !plEntry) { + if (!plEntry) { re += '\\)' continue } @@ -1192,7 +1163,7 @@ export class Minimatch { case '|': { const plEntry = patternListStack[patternListStack.length - 1] - if (inClass || !plEntry) { + if (!plEntry) { re += '\\|' continue } @@ -1211,75 +1182,30 @@ export class Minimatch { case '[': // swallow any state-tracking char before the [ clearStateChar() - - if (inClass) { - re += '\\' + c - continue + const [src, needUflag, consumed] = parseClass(pattern, i) + if (consumed) { + re += src + uflag = uflag || needUflag + i += consumed - 1 + hasMagic = true + } else { + re += '\\[' } - - inClass = true - classStart = i - reClassStart = re.length - re += c continue case ']': - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += '\\' + c - continue - } - - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + braExpEscape(charUnescape(cs)) + ']') - // looks good, finish up the class. - re += c - } catch (er) { - // out of order ranges in JS are errors, but in glob syntax, - // they're just a range that matches nothing. - re = re.substring(0, reClassStart) + '(?:$.)' // match nothing ever - } - hasMagic = true - inClass = false + re += '\\' + c continue default: // swallow any state char that wasn't consumed clearStateChar() - if (reSpecials[c] && !(c === '^' && inClass)) { - re += '\\' - } - - re += c + re += regExpEscape(c) break } // switch } // for - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - cs = pattern.slice(classStart + 1) - sp = this.parse(cs, SUBPARSE) as SubparseReturn - re = re.substring(0, reClassStart) + '\\[' + sp[0] - hasMagic = hasMagic || sp[1] - } - // handle the case where we had a +( thing at the *end* // of the pattern. // each pattern list stack adds 3 chars, and we need to go through @@ -1352,7 +1278,7 @@ export class Minimatch { } nlAfter = cleanAfter - const dollar = nlAfter === '' && isSub !== SUBPARSE ? '(?:$|\\/)' : '' + const dollar = nlAfter === '' ? '(?:$|\\/)' : '' re = nlBefore + nlFirst + nlAfter + dollar + nlLast } @@ -1368,11 +1294,6 @@ export class Minimatch { re = patternStart() + re } - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [re, hasMagic] - } - // if it's nocase, and the lcase/uppercase don't match, it's magic if (options.nocase && !hasMagic && !options.nocaseMagicOnly) { hasMagic = pattern.toUpperCase() !== pattern.toLowerCase() @@ -1385,7 +1306,7 @@ export class Minimatch { return globUnescape(pattern) } - const flags = options.nocase ? 'i' : '' + const flags = (options.nocase ? 'i' : '') + (uflag ? 'u' : '') try { const ext = fastTest ? { diff --git a/tap-snapshots/test/basic.js.test.cjs b/tap-snapshots/test/basic.js.test.cjs index 4f594f1b..574b206f 100644 --- a/tap-snapshots/test/basic.js.test.cjs +++ b/tap-snapshots/test/basic.js.test.cjs @@ -34,7 +34,7 @@ false ` exports[`test/basic.js TAP basic tests > makeRe #* 1`] = ` -/^(?:(?=.)#[^/]*?)$/ +/^(?:(?=.)\\#[^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe * 1`] = ` @@ -338,7 +338,7 @@ exports[`test/basic.js TAP basic tests > makeRe @(a|a[(])b 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe @(a|a[)])b 1`] = ` -/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[\\)])b)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[)])b)$/ ` exports[`test/basic.js TAP basic tests > makeRe @(js|.*) 1`] = ` @@ -362,19 +362,47 @@ exports[`test/basic.js TAP basic tests > makeRe [ 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe [!a* 1`] = ` -/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/ +/^(?:(?=.)\\[\\!a[^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe [#a* 1`] = ` -/^(?:(?=.)\\[(?=.)#a[^/]*?)$/ +/^(?:(?=.)\\[\\#a[^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe [* 1`] = ` -/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/ +/^(?:(?=.)\\[[^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe [-abc] 1`] = ` -/^(?:(?!\\.)(?=.)[-abc])$/ +/^(?:(?!\\.)(?=.)[\\-abc])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:graph:][:digit:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)([\\p{Nd}]|[^\\p{Z}\\p{C}])f[^/]*?)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:graph:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)[^\\p{Z}\\p{C}]f[^/]*?)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]]??? 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][^/][^/][^/])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9])$/ ` exports[`test/basic.js TAP basic tests > makeRe [[] 1`] = ` @@ -390,11 +418,11 @@ exports[`test/basic.js TAP basic tests > makeRe [\\\\] 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe [\\b-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/basic.js TAP basic tests > makeRe [\\z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/basic.js TAP basic tests > makeRe [] 1`] = ` @@ -406,7 +434,7 @@ exports[`test/basic.js TAP basic tests > makeRe []+*] 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe []-] 1`] = ` -/^(?:(?!\\.)(?=.)[\\]-])$/ +/^(?:(?!\\.)(?=.)[\\]\\-])$/ ` exports[`test/basic.js TAP basic tests > makeRe []] 1`] = ` @@ -418,11 +446,15 @@ exports[`test/basic.js TAP basic tests > makeRe [^a-c]* 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe [a-0][a-Ā] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.)[a-Ā])$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [a-[:alpha:]*] 1`] = ` +/^(?:(?=.)$.)$/ ` exports[`test/basic.js TAP basic tests > makeRe [a-b-c] 1`] = ` -/^(?:(?!\\.)(?=.)[a-b-c])$/ +/^(?:(?!\\.)(?=.)[a-b\\-c])$/ ` exports[`test/basic.js TAP basic tests > makeRe [a-c]b* 1`] = ` @@ -442,7 +474,19 @@ exports[`test/basic.js TAP basic tests > makeRe [abc 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe [abc-] 1`] = ` -/^(?:(?!\\.)(?=.)[abc-])$/ +/^(?:(?!\\.)(?=.)[abc\\-])$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [f-fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [f-gz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f-g][^/]*?)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe [ia]?[ck] 1`] = ` @@ -450,7 +494,15 @@ exports[`test/basic.js TAP basic tests > makeRe [ia]?[ck] 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe [z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [z-a]* 1`] = ` +/^(?:(?=.)$.)$/ +` + +exports[`test/basic.js TAP basic tests > makeRe [z-af]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/basic.js TAP basic tests > makeRe [z\\-a] 1`] = ` @@ -546,7 +598,7 @@ exports[`test/basic.js TAP basic tests > makeRe a/[2015-03-10T00:23:08.647Z\\]/z ` exports[`test/basic.js TAP basic tests > makeRe a/[2015-03-10T00:23:08.647Z]/z 1`] = ` -/^(?:a\\/(?!\\.)(?=.)(?:$.)\\/z)$/ +/^(?:a\\/(?!\\.)(?=.)[2010T00:23:08.647Z]\\/z)$/ ` exports[`test/basic.js TAP basic tests > makeRe a?b 1`] = ` @@ -558,7 +610,7 @@ exports[`test/basic.js TAP basic tests > makeRe a?c 1`] = ` ` exports[`test/basic.js TAP basic tests > makeRe a[X-]b 1`] = ` -/^(?:(?=.)a[X-]b)$/ +/^(?:(?=.)a[X\\-]b)$/ ` exports[`test/basic.js TAP basic tests > makeRe a[\\b]c 1`] = ` diff --git a/tap-snapshots/test/optimization-level-0.ts.test.cjs b/tap-snapshots/test/optimization-level-0.ts.test.cjs index 404ce947..94c10cda 100644 --- a/tap-snapshots/test/optimization-level-0.ts.test.cjs +++ b/tap-snapshots/test/optimization-level-0.ts.test.cjs @@ -705,6 +705,64 @@ Array [ ] ` +exports[`test/optimization-level-0.ts TAP basic tests > "[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]" ["0f7fa","99999","aeiou","fffff","åéîøü"] 1`] = ` +Array [ + "0f7fa", + "99999", + "aeiou", + "fffff", + "åéîøü", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]" ["aeiou","fffff","åéîøü"] 1`] = ` +Array [ + "aeiou", + "fffff", + "åéîøü", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]]" ["0f7fa","99999","aeiou","fffff"] 1`] = ` +Array [ + "0f7fa", + "99999", + "aeiou", + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:graph:][:digit:]]f*" ["0f7fa","fffff"] 1`] = ` +Array [ + "0f7fa", + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:graph:]]f*" ["0f7fa","fffff"] 1`] = ` +Array [ + "0f7fa", + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:xdigit:]][[:xdigit:]]???" ["0f7fa","99999","aeiou","fffff"] 1`] = ` +Array [ + "0f7fa", + "99999", + "aeiou", + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]" ["0f7fa","99999","fffff"] 1`] = ` +Array [ + "0f7fa", + "99999", + "fffff", +] +` + exports[`test/optimization-level-0.ts TAP basic tests > "[[]" ["["] 1`] = ` Array [ "[", @@ -761,6 +819,10 @@ exports[`test/optimization-level-0.ts TAP basic tests > "[a-0][a-Ā]" [] 1`] = ` Array [] ` +exports[`test/optimization-level-0.ts TAP basic tests > "[a-[:alpha:]*]" [] 1`] = ` +Array [] +` + exports[`test/optimization-level-0.ts TAP basic tests > "[a-b-c]" [] 1`] = ` Array [] ` @@ -805,6 +867,24 @@ Array [ ] ` +exports[`test/optimization-level-0.ts TAP basic tests > "[f-fz-a]*" ["fffff"] 1`] = ` +Array [ + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[f-gz-a]*" ["fffff"] 1`] = ` +Array [ + "fffff", +] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[fz-a]*" ["fffff"] 1`] = ` +Array [ + "fffff", +] +` + exports[`test/optimization-level-0.ts TAP basic tests > "[ia]?[ck]" ["ABC","IjK"] 1`] = ` Array [ "ABC", @@ -816,6 +896,16 @@ exports[`test/optimization-level-0.ts TAP basic tests > "[z-a]" [] 1`] = ` Array [] ` +exports[`test/optimization-level-0.ts TAP basic tests > "[z-a]*" [] 1`] = ` +Array [] +` + +exports[`test/optimization-level-0.ts TAP basic tests > "[z-af]*" ["fffff"] 1`] = ` +Array [ + "fffff", +] +` + exports[`test/optimization-level-0.ts TAP basic tests > "[z\\\\-a]" [] 1`] = ` Array [] ` @@ -1159,7 +1249,7 @@ false ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe #* 1`] = ` -/^(?:(?=.)#[^/]*?)$/ +/^(?:(?=.)\\#[^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe * 1`] = ` @@ -1463,7 +1553,7 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe @(a|a[(])b 1`] = ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe @(a|a[)])b 1`] = ` -/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[\\)])b)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[)])b)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe @(js|.*) 1`] = ` @@ -1487,19 +1577,47 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe [ 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [!a* 1`] = ` -/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/ +/^(?:(?=.)\\[\\!a[^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [#a* 1`] = ` -/^(?:(?=.)\\[(?=.)#a[^/]*?)$/ +/^(?:(?=.)\\[\\#a[^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [* 1`] = ` -/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/ +/^(?:(?=.)\\[[^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [-abc] 1`] = ` -/^(?:(?!\\.)(?=.)[-abc])$/ +/^(?:(?!\\.)(?=.)[\\-abc])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:graph:][:digit:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)([\\p{Nd}]|[^\\p{Z}\\p{C}])f[^/]*?)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:graph:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)[^\\p{Z}\\p{C}]f[^/]*?)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]]??? 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][^/][^/][^/])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9])$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [[] 1`] = ` @@ -1515,11 +1633,11 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe [\\\\] 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [\\b-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [\\z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [] 1`] = ` @@ -1531,7 +1649,7 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe []+*] 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe []-] 1`] = ` -/^(?:(?!\\.)(?=.)[\\]-])$/ +/^(?:(?!\\.)(?=.)[\\]\\-])$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe []] 1`] = ` @@ -1543,11 +1661,15 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe [^a-c]* 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [a-0][a-Ā] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.)[a-Ā])$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [a-[:alpha:]*] 1`] = ` +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [a-b-c] 1`] = ` -/^(?:(?!\\.)(?=.)[a-b-c])$/ +/^(?:(?!\\.)(?=.)[a-b\\-c])$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [a-c]b* 1`] = ` @@ -1567,7 +1689,19 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe [abc 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [abc-] 1`] = ` -/^(?:(?!\\.)(?=.)[abc-])$/ +/^(?:(?!\\.)(?=.)[abc\\-])$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [f-fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [f-gz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f-g][^/]*?)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [ia]?[ck] 1`] = ` @@ -1575,7 +1709,15 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe [ia]?[ck] 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [z-a]* 1`] = ` +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-0.ts TAP basic tests > makeRe [z-af]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe [z\\-a] 1`] = ` @@ -1671,7 +1813,7 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe a/[2015-03-10T00: ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe a/[2015-03-10T00:23:08.647Z]/z 1`] = ` -/^(?:a\\/(?!\\.)(?=.)(?:$.)\\/z)$/ +/^(?:a\\/(?!\\.)(?=.)[2010T00:23:08.647Z]\\/z)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe a?b 1`] = ` @@ -1683,7 +1825,7 @@ exports[`test/optimization-level-0.ts TAP basic tests > makeRe a?c 1`] = ` ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe a[X-]b 1`] = ` -/^(?:(?=.)a[X-]b)$/ +/^(?:(?=.)a[X\\-]b)$/ ` exports[`test/optimization-level-0.ts TAP basic tests > makeRe a[\\b]c 1`] = ` diff --git a/tap-snapshots/test/optimization-level-2.ts.test.cjs b/tap-snapshots/test/optimization-level-2.ts.test.cjs index 798fac79..15f670fb 100644 --- a/tap-snapshots/test/optimization-level-2.ts.test.cjs +++ b/tap-snapshots/test/optimization-level-2.ts.test.cjs @@ -34,7 +34,7 @@ false ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe #* 1`] = ` -/^(?:(?=.)#[^/]*?)$/ +/^(?:(?=.)\\#[^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe * 1`] = ` @@ -338,7 +338,7 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe @(a|a[(])b 1`] = ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe @(a|a[)])b 1`] = ` -/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[\\)])b)$/ +/^(?:(?=.)(?:(?!\\.)a|(?!\\.)a[)])b)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe @(js|.*) 1`] = ` @@ -362,19 +362,47 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe [ 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [!a* 1`] = ` -/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/ +/^(?:(?=.)\\[\\!a[^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [#a* 1`] = ` -/^(?:(?=.)\\[(?=.)#a[^/]*?)$/ +/^(?:(?=.)\\[\\#a[^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [* 1`] = ` -/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/ +/^(?:(?=.)\\[[^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [-abc] 1`] = ` -/^(?:(?!\\.)(?=.)[-abc])$/ +/^(?:(?!\\.)(?=.)[\\-abc])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}][\\p{L}\\p{Nl}\\p{Nd}])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}][\\p{L}\\p{Nl}])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]] 1`] = ` +/^(?:(?!\\.)(?=.)[\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f][\\x00-\\x7f])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:graph:][:digit:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)([\\p{Nd}]|[^\\p{Z}\\p{C}])f[^/]*?)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:graph:]]f* 1`] = ` +/^(?:(?!\\.)(?=.)[^\\p{Z}\\p{C}]f[^/]*?)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]]??? 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][^/][^/][^/])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] 1`] = ` +/^(?:(?!\\.)(?=.)[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9])$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [[] 1`] = ` @@ -390,11 +418,11 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe [\\\\] 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [\\b-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [\\z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [] 1`] = ` @@ -406,7 +434,7 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe []+*] 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe []-] 1`] = ` -/^(?:(?!\\.)(?=.)[\\]-])$/ +/^(?:(?!\\.)(?=.)[\\]\\-])$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe []] 1`] = ` @@ -418,11 +446,15 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe [^a-c]* 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [a-0][a-Ā] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.)[a-Ā])$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [a-[:alpha:]*] 1`] = ` +/^(?:(?=.)$.)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [a-b-c] 1`] = ` -/^(?:(?!\\.)(?=.)[a-b-c])$/ +/^(?:(?!\\.)(?=.)[a-b\\-c])$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [a-c]b* 1`] = ` @@ -442,7 +474,19 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe [abc 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [abc-] 1`] = ` -/^(?:(?!\\.)(?=.)[abc-])$/ +/^(?:(?!\\.)(?=.)[abc\\-])$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [f-fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [f-gz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f-g][^/]*?)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [fz-a]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [ia]?[ck] 1`] = ` @@ -450,7 +494,15 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe [ia]?[ck] 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [z-a] 1`] = ` -/^(?:(?!\\.)(?=.)(?:$.))$/ +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [z-a]* 1`] = ` +/^(?:(?=.)$.)$/ +` + +exports[`test/optimization-level-2.ts TAP basic tests > makeRe [z-af]* 1`] = ` +/^(?:(?!\\.)(?=.)[f][^/]*?)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe [z\\-a] 1`] = ` @@ -546,7 +598,7 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe a/[2015-03-10T00: ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe a/[2015-03-10T00:23:08.647Z]/z 1`] = ` -/^(?:a\\/(?!\\.)(?=.)(?:$.)\\/z)$/ +/^(?:a\\/(?!\\.)(?=.)[2010T00:23:08.647Z]\\/z)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe a?b 1`] = ` @@ -558,7 +610,7 @@ exports[`test/optimization-level-2.ts TAP basic tests > makeRe a?c 1`] = ` ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe a[X-]b 1`] = ` -/^(?:(?=.)a[X-]b)$/ +/^(?:(?=.)a[X\\-]b)$/ ` exports[`test/optimization-level-2.ts TAP basic tests > makeRe a[\\b]c 1`] = ` diff --git a/test/patterns.js b/test/patterns.js index 030415d9..34408e70 100644 --- a/test/patterns.js +++ b/test/patterns.js @@ -415,6 +415,36 @@ module.exports = [ ['x/*/../a/b/c', ['x/a/b/c']], ['x/z/../*/a/b/c', ['x/y/a/b/c', 'x/z/a/b/c']], ['x/*/../../a/b/c', ['a/b/c']], + + () => (files = ['åéîøü', 'aeiou', 'fffff', '0f7fa', '99999']), + [ + '[[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]', + ['åéîøü', 'aeiou', 'fffff'], + ], + [ + '[[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]][[:ascii:]]', + ['aeiou', 'fffff', '0f7fa', '99999'], + ], + ['[a-[:alpha:]*]', []], + ['[z-a]*', []], + ['[z-af]*', ['fffff']], + ['[fz-a]*', ['fffff']], + ['[f-gz-a]*', ['fffff']], + ['[f-fz-a]*', ['fffff']], + [ + '[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]', + ['fffff', '0f7fa', '99999'], + ], + [ + '[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]', + ['åéîøü', 'aeiou', 'fffff', '0f7fa', '99999'], + ], + [ + '[[:xdigit:]][[:xdigit:]]???', + ['aeiou', 'fffff', '0f7fa', '99999'], + ], + [ '[[:graph:]]f*', ['fffff', '0f7fa']], + [ '[[:graph:][:digit:]]f*', ['fffff', '0f7fa']], ] Object.defineProperty(module.exports, 'files', {