UNPKG

@sap/cds-dk

Version:

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

78 lines (71 loc) 2.86 kB
const path = require('node:path'); const { join } = path; const { existsSync } = require('node:fs'); const { readdir } = require('node:fs').promises; /** * Returns an AsyncIterator of those (recursive) subdirectories that match the glob pattern. * @param pattern - The glob pattern to match against. * @param cwd - The current working directory to start the search from. * @returns {AsyncIterator<string>} - 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. * @param pattern - The glob pattern to match against. * @param 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)); } /** * Recursively returns all subdirectories of a given directory. * Returns only non-hidden directories (those not starting with a dot). * @param {import('fs').PathLike} dir - The directory to search in. * @returns {Promise<String[]>} - An array of paths to subdirectories. */ async function subdirs(dir) { if (!existsSync(dir)) return []; const children = (await readdir(dir, { withFileTypes: true })) .filter(child => child.isDirectory() && !child.name.startsWith('.')); 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 };