UNPKG

nano-staged

Version:

Tiny tool to run commands for modified, staged, and committed git files.

265 lines (221 loc) 5.45 kB
const CLOSE_PARENTHESES = ')'.charCodeAt(0) const OPEN_PARENTHESES = '('.charCodeAt(0) const CLOSE_SQUARE = ']'.charCodeAt(0) const OPEN_SQUARE = '['.charCodeAt(0) const CLOSE_CURLY = '}'.charCodeAt(0) const OPEN_CURLY = '{'.charCodeAt(0) const BACKSLASH = '\\'.charCodeAt(0) const ASTERISK = '*'.charCodeAt(0) const QUESTION = '?'.charCodeAt(0) const DOLLAR = '$'.charCodeAt(0) const EQUALS = '='.charCodeAt(0) const CARRET = '^'.charCodeAt(0) const SLASH = '/'.charCodeAt(0) const COLON = ':'.charCodeAt(0) const POINT = '.'.charCodeAt(0) const PIPE = '|'.charCodeAt(0) const PLUS = '+'.charCodeAt(0) const BANG = '!'.charCodeAt(0) const COMA = ','.charCodeAt(0) const AT = '@'.charCodeAt(0) const GLOBSTAR = `((?:[^/]*(?:/|$))*)` const WILDCARD = `([^/]*)` export function globToRegex(glob, opts = {}) { let { extended = false, globstar = false, flags = '' } = opts let inRange = false let inGroup = false let stack = [] let regex = '' let pos = 0 let code, next while (pos < glob.length) { code = glob.charCodeAt(pos) switch (code) { case BACKSLASH: case DOLLAR: case CARRET: case EQUALS: case POINT: { regex += `\\${glob[pos]}` break } case SLASH: { regex += `\\${glob[pos]}` if (glob.charCodeAt(pos + 1) === SLASH) { regex += '?' } break } case OPEN_PARENTHESES: { if (stack.length) { regex += glob[pos] break } regex += `\\${glob[pos]}` break } case CLOSE_PARENTHESES: { if (stack.length) { regex += glob[pos] let type = stack.pop() if (type === '@') { regex += '{1}' } else if (type === '!') { regex += '([^/]*)' } else { regex += type } break } regex += `\\${glob[pos]}` break } case PIPE: { if (stack.length) { regex += glob[pos] break } regex += `\\${glob[pos]}` break } case PLUS: { if (glob.charCodeAt(pos + 1) === OPEN_PARENTHESES && extended) { stack.push(glob[pos]) break } regex += `\\${glob[pos]}` break } case AT: { if (glob.charCodeAt(pos + 1) === OPEN_PARENTHESES && extended) { stack.push(glob[pos]) break } } case BANG: { if (extended) { if (inRange) { regex += `^` break } if (glob.charCodeAt(pos + 1) === OPEN_PARENTHESES) { stack.push(glob[pos]) regex += `(?!` pos++ break } regex += `\\${glob[pos]}` break } regex += `\\${glob[pos]}` break } case QUESTION: { if (extended) { if (glob.charCodeAt(pos + 1) === OPEN_PARENTHESES) { stack.push(glob[pos]) } else { regex += `.` } break } regex += `\\${glob[pos]}` break } case OPEN_SQUARE: { if (inRange && glob.charCodeAt(pos + 1) === COLON) { next = glob.indexOf(':', pos + 2) let value = glob.slice(pos + 2, next) if (value === 'alnum') { regex += `(\\w|\\d)` } else if (value === 'space') { regex += `\\s` } else if (value === 'digit') { regex += `\\d` } pos = next + 1 break } if (extended) { inRange = true regex += glob[pos] break } regex += `\\${glob[pos]}` break } case CLOSE_SQUARE: { if (extended) { inRange = false regex += glob[pos] break } regex += `\\${glob[pos]}` break } case OPEN_CURLY: { if (extended) { inGroup = true regex += `(` break } regex += `\\${glob[pos]}` break } case CLOSE_CURLY: { if (extended) { inGroup = false regex += `)` break } regex += `\\${glob[pos]}` break } case COMA: { if (inGroup) { regex += `|` break } regex += `\\${glob[pos]}` break } case ASTERISK: { if (glob.charCodeAt(pos + 1) === OPEN_PARENTHESES && extended) { stack.push(glob[pos]) break } let prevChar = glob[pos - 1] let starCount = 1 while (glob.charCodeAt(pos + 1) === ASTERISK) { starCount++ pos++ } let nextChar = glob[pos + 1] if (!globstar) { regex += `.*` } else { let isGlobstar = starCount > 1 && (prevChar === '/' || prevChar === undefined) && (nextChar === '/' || nextChar === undefined) if (isGlobstar) { regex += GLOBSTAR pos++ } else { regex += WILDCARD } } break } default: { regex += glob[pos] break } } pos++ } if (!flags.includes('g')) { regex = `^${regex}$` } return { regex: new RegExp(regex, flags) } }