UNPKG

recrawl-sync

Version:

[![npm](https://img.shields.io/npm/v/recrawl-sync.svg)](https://www.npmjs.com/package/recrawl-sync) [![ci](https://github.com/aleclarson/recrawl/actions/workflows/release.yml/badge.svg)](https://github.com/aleclarson/recrawl/actions/workflows/release.yml)

201 lines 6.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createMatcher = exports.compileGlob = exports.recrawl = exports.crawl = void 0; const tslib_1 = require("tslib"); const relative_1 = require("@cush/relative"); const fs = tslib_1.__importStar(require("fs")); const glob_regex_1 = tslib_1.__importDefault(require("glob-regex")); const slash_1 = tslib_1.__importDefault(require("slash")); const path = tslib_1.__importStar(require("path")); const fs_1 = require("./fs"); const { S_IFMT, S_IFLNK, S_IFDIR } = fs.constants; const limitDepth = (limit) => (_, depth) => depth < limit; const alwaysTrue = () => true; const alwaysFalse = () => false; /** Create a crawl function, and crawl the given root immediately */ const crawl = (root, opts = {}) => recrawl(opts)(root); exports.crawl = crawl; /** Create a crawl function */ function recrawl(opts = {}) { const only = createMatcher(opts.only) || alwaysTrue; const skip = createMatcher(opts.skip) || alwaysFalse; const fs = opts.adapter || fs_1.localFs; const enter = opts.enter || alwaysTrue; const filter = opts.filter || alwaysTrue; const follow = createFollower(opts); const maxDepth = typeof opts.depth == 'number' ? Math.max(0, opts.depth) : opts.deep === false ? 0 : Infinity; return (root, arg) => { root = (0, slash_1.default)(path.resolve(root)) + '/'; let each; let files; if (typeof arg == 'function') { each = arg; } else { files = arg || (follow ? {} : []); each = Array.isArray(files) ? file => files.push(file) : (file, link) => { files[file] = link !== null ? link : true; }; } let depth = 0; const crawl = (dir) => { for (const name of fs.readdir(root + dir)) { let file = dir + name; if (skip(file, name)) { continue; } let mode; try { mode = fs.stat(root + file).mode & S_IFMT; } catch (err) { if (err.code == 'ENOENT' || err.code == 'ELOOP') { continue; // Ignore broken symlinks. } throw err; } if (mode == S_IFDIR) { if (depth == maxDepth) { continue; } if (enter(file, depth)) { depth++; try { crawl(file + '/'); } catch (err) { if (err.code !== 'EACCES') { throw err; } } depth--; } } else if (only(file, name) && filter(file, name)) { let link = null; if (follow) { mode = fs.lstat(root + file).mode & S_IFMT; if (mode === S_IFLNK) link = follow(file, root); } if (opts.absolute) { file = root + file; } each(file, link); } } }; crawl(''); return files; }; } exports.recrawl = recrawl; /** Tests true for absolute paths and globs starting with two asterisks. */ const globAllRE = /(?:\/|\*\*)/; /** Merge regular expressions together. */ const matchAny = (patterns) => new RegExp(`^(?:${patterns.join('|')})$`); /** * Compile a single Recrawl glob string into its "RegExp pattern" equivalent. * * Note: This is only useful for globs with "/" or "**" in them. */ function compileGlob(glob, root) { if (glob[0] == '/') { glob = glob.slice(1); } else if (glob[0] !== '*') { glob = '**/' + glob; } if (glob.endsWith('/')) { glob += '**'; } if (root) glob = path.posix.join(root, glob); return glob_regex_1.default.replace(glob); } exports.compileGlob = compileGlob; /** * Create a function that tests against an array of Recrawl glob strings by * compiling them into RegExp objects. */ function createMatcher(globs, root) { if (!globs || !globs.length) { return null; } const fileGlobs = []; const nameGlobs = []; globs.forEach(glob => { if (glob instanceof RegExp) { fileGlobs.push(glob.source); } else if (globAllRE.test(glob)) { fileGlobs.push(compileGlob(glob, root)); } else { nameGlobs.push(glob_regex_1.default.replace(glob)); } }); const fileRE = fileGlobs.length ? matchAny(fileGlobs) : false; const nameRE = nameGlobs.length ? matchAny(nameGlobs) : false; return (file, name) => (nameRE && nameRE.test(name || path.basename(file))) || (fileRE && fileRE.test(file)); } exports.createMatcher = createMatcher; // Create a function that follows symlinks. function createFollower(opts) { const fs = opts.adapter || fs_1.localFs; const filter = opts.follow === true ? alwaysTrue : typeof opts.follow == 'number' ? limitDepth(opts.follow) : typeof opts.follow == 'function' ? opts.follow : null; // The "name" argument must be relative to the "root" argument. if (!filter) return null; return (name, root) => { let depth = 0; if (name === null || !filter(name, depth)) { return null; } let link = root + name; let mode; do { let target = (0, slash_1.default)(fs.readlink(link)); if (path.isAbsolute(target)) { name = null; link = target; } // When "target" is relative, resolve it. else if (name !== null) { // The "relative" function expects ./ or ../ if (!/^\.\.?\//.test(target[0])) { target = './' + target; } // This code path is faster. name = (0, relative_1.relative)(name, target); link = root + name; } else { link = (0, slash_1.default)(path.resolve(path.dirname(link), target)); } try { mode = fs.lstat(link).mode & S_IFMT; if (mode !== S_IFLNK) break; } catch (_a) { break; } } while (filter(name == null ? link : name, ++depth)); return name == null ? link : name; }; } //# sourceMappingURL=recrawl.js.map