UNPKG

expandable

Version:

UNIX shell style file globbing in that painlessly ports to Windows.

113 lines (98 loc) 2.99 kB
var path = require('path') var fs = require('fs') var assert = require('assert') var REGEX = new RegExp('(\\' + '/ . * + ? | ( ) [ ] { } \\'.split(' ').join('|\\') + ')', 'g') function regular (text) { return text.replace(REGEX, '\\$1') } function find (base, directory, pattern, index, found) { if (index == pattern.length) { found.push(path.relative(base, directory)) return } if (pattern[index]('.')) { find(base, directory, pattern, index + 1, found) return } if (pattern[index]('..')) { find(base, path.dirname(directory), pattern, index + 1, found) return } if (!fs.statSync(directory).isDirectory()) { return } fs.readdirSync(directory).forEach(function (file) { if (pattern[index](file)) { find(base, path.resolve(directory, file), pattern, index + 1, found) } }) } function explode (pattern) { var exploded = [] pattern.split('/').forEach(function (unix) { unix.split('\\').forEach(function (part) { exploded.push(part) }) }) return exploded } function alternation ($) { var regex = '(', $1 while ($1 = /^([^{}]*)([{}])(.*)$/.exec($[3])) { $ = $1 regex += $[1].split(',').map(function (alt) { return compile(alt) }).join('|') if ($[2] == '}') { regex += ')' } else { $ = alternation($) regex += $[1] } } $[1] = regex return $ } function compile (glob) { var $, regex = '' while ($ = /^(.*?)(\*|\?|\[[^\]]+\]|{)(.*)$/.exec(glob)) { regex += regular($[1]) switch ($[2][0]) { case '*': regex += '.*' break case '?': regex += '.' break case '[': regex += $[2].replace(/^\[!/, '[^') break case '{': $ = alternation($) regex += $[1] break } glob = $[3] } return regex + regular(glob) } function glob (directory, argv) { var found = [] argv.forEach(function (pattern) { var exploded = explode(pattern), match var compiled = exploded.map(function (part) { var regex if (/^\.\.?$/.test(part)) { return function (file) { return file == part } } regex = new RegExp('^' + compile(part) + '$') return function (file) { return !/^\.\.?/.test(file) && regex.test(file) } }) found.push(match = { path: pattern, files: [] }) exploded = explode(directory) // TODO Make it work with relative file paths. assert(exploded[0] == '') // Leading slash is necessary because of join. exploded[0] = '/' var normalized = path.normalize(path.join.apply(path, exploded)) find(normalized, normalized, compiled, 0, match.files) }) return found } module.exports = glob