@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
78 lines (71 loc) • 2.86 kB
JavaScript
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
};