UNPKG

@sap/cds-dk

Version:

Command line client and development toolkit for the SAP Cloud Application Programming Model

92 lines (81 loc) 3.29 kB
const path = require('node:path'); const { join } = path; const { readdir } = require('node:fs').promises; const IGNORE_DIRS = new Set(['node_modules', '.git', '.hg', '.svn', 'dist', 'target', 'build', 'out']); /** * Returns an AsyncIterator of those (recursive) subdirectories that match the glob pattern. * Skips common large directories (node_modules, .git, .hg, .svn, dist, target, build, out). * @param {string} pattern - The glob pattern to match against. * @param {object} options * @param {string} options.cwd - The current working directory to start the search from. * @returns An array of relative paths that match the glob pattern. */ async function* glob(pattern, { cwd } = { cwd: process.cwd() }) { if (/\{.*}/.test(pattern)) { // We rely on the native Node.js glob support for brace expansion. const { glob: globNative } = require('node:fs/promises'); if (!globNative) { throw `Glob pattern '${pattern} requires native Node.js glob support to resolve, which is available in Node.js version 22, but project uses ${process.version}.` } return globNative(pattern, { cwd }); } yield* (await globArray(pattern, cwd)); } /** * Returns those subdirectories, recursively, that match the glob pattern. * Skips common large directories (node_modules, .git, .hg, .svn, dist, target, build, out). * @param {string} pattern - The glob pattern to match against. * @param {string} cwd - The current working directory to start the search from. * @returns {Promise<string[]>} - An array of relative paths that match the glob pattern. */ async function globArray(pattern, cwd) { const regex = globToRegex(pattern); return (await subdirs(cwd)) .map(fullPath => path.relative(cwd, fullPath)) .filter(relativePath => regex.test(relativePath.replace(/\\/g, '/'))); } /** * Recursively returns all subdirectories of a given directory. * Returns only non-hidden directories (those not starting with a dot). * Skips common large directories like node_modules, .git, etc. * @param {string} dir - The directory to search in. * @returns {Promise<String[]>} - An array of paths to subdirectories. */ async function subdirs(dir) { let entries; try { entries = await readdir(dir, { withFileTypes: true }); } catch { return []; } const children = entries.filter(child => child.isDirectory() && !child.name.startsWith('.') && !IGNORE_DIRS.has(child.name) ); return (await Promise.all( children.map(async child => { const childPath = join(dir, child.name); const descendants = await subdirs(childPath); return [childPath, ...descendants]; }) )).flat(); } /** * Converts a glob pattern to a regular expression. * Supports the following wildcards: `? * ** [...] [!...]`. * Patterns such as `./foo` are normalized to `foo`. * @param {string} glob - The glob pattern to convert. * @returns {RegExp} - The regular expression that matches the glob pattern. */ function globToRegex(glob) { const regex = glob .replace(/^\.\//, '') .replaceAll('**', '|') .replaceAll(/\[!([^\]]+)]/g, '[^$1]') .replaceAll('?', '[^/]') .replaceAll('*', '[^/]*') .replaceAll('|', '.*'); return new RegExp(`^${regex}$`); } module.exports = { glob };