@dbs-portal/core-module-registry
Version:
Core module registry system for automatic module discovery and registration
213 lines • 7.3 kB
JavaScript
/**
* Browser-compatible glob utilities
*
* Provides glob pattern matching that works in browser environments
* while maintaining compatibility with the glob library API.
*/
class BrowserGlob {
fileCache = new Map();
directoryCache = new Map();
/**
* Main glob function - browser compatible
*/
async glob(pattern, options = {}) {
const patterns = Array.isArray(pattern) ? pattern : [pattern];
const results = [];
const cwd = options.cwd || '/';
for (const pat of patterns) {
const matches = await this.matchPattern(pat, cwd, options);
results.push(...matches);
}
// Remove duplicates and apply filters
const uniqueResults = [...new Set(results)];
return this.applyFilters(uniqueResults, options);
}
/**
* Synchronous glob (limited functionality in browser)
*/
globSync(pattern, options = {}) {
// In browser, we can only work with cached data
const patterns = Array.isArray(pattern) ? pattern : [pattern];
const results = [];
const cwd = options.cwd || '/';
for (const pat of patterns) {
const matches = this.matchPatternSync(pat, cwd, options);
results.push(...matches);
}
const uniqueResults = [...new Set(results)];
return this.applyFilters(uniqueResults, options);
}
/**
* Match a single pattern
*/
async matchPattern(pattern, cwd, options) {
// For browser environment, we work with pre-loaded file lists
const allFiles = this.getAllCachedFiles(cwd);
return this.filterByPattern(allFiles, pattern, options);
}
/**
* Match a single pattern synchronously
*/
matchPatternSync(pattern, cwd, options) {
const allFiles = this.getAllCachedFiles(cwd);
return this.filterByPattern(allFiles, pattern, options);
}
/**
* Filter files by glob pattern
*/
filterByPattern(files, pattern, options) {
const regex = this.globToRegex(pattern);
const matches = [];
for (const file of files) {
if (regex.test(file)) {
// Apply additional filters
if (options.onlyDirectories && !this.isDirectory(file))
continue;
if (options.onlyFiles && this.isDirectory(file))
continue;
if (!options.dot && this.isDotFile(file))
continue;
if (options.ignore && this.matchesIgnorePattern(file, options.ignore))
continue;
matches.push(options.absolute ? file : this.makeRelative(file, options.cwd || '/'));
}
}
return matches;
}
/**
* Convert glob pattern to regex
*/
globToRegex(pattern) {
let regexPattern = pattern
.replace(/\./g, '\\.')
.replace(/\*\*/g, '___DOUBLESTAR___')
.replace(/\*/g, '[^/]*')
.replace(/___DOUBLESTAR___/g, '.*')
.replace(/\?/g, '[^/]');
// Handle character classes [abc]
regexPattern = regexPattern.replace(/\[([^\]]+)\]/g, '[$1]');
// Handle negation [!abc] -> [^abc]
regexPattern = regexPattern.replace(/\[!([^\]]+)\]/g, '[^$1]');
// Handle braces {a,b,c} -> (a|b|c)
regexPattern = regexPattern.replace(/\{([^}]+)\}/g, (_match, content) => {
const alternatives = content.split(',').map((alt) => alt.trim());
return `(${alternatives.join('|')})`;
});
return new RegExp(`^${regexPattern}$`);
}
/**
* Get all cached files for a directory
*/
getAllCachedFiles(cwd) {
const files = [];
// Get files from file cache
for (const [path, _content] of this.fileCache) {
if (path.startsWith(cwd)) {
files.push(path);
}
}
// Get directories from directory cache
for (const [path, dirFiles] of this.directoryCache) {
if (path.startsWith(cwd)) {
files.push(path);
files.push(...dirFiles.map(f => `${path}/${f}`));
}
}
return files;
}
/**
* Apply additional filters
*/
applyFilters(files, options) {
return files.filter(file => {
if (options.onlyDirectories && !this.isDirectory(file))
return false;
if (options.onlyFiles && this.isDirectory(file))
return false;
if (!options.dot && this.isDotFile(file))
return false;
if (options.ignore && this.matchesIgnorePattern(file, options.ignore))
return false;
return true;
});
}
/**
* Check if path is a directory
*/
isDirectory(path) {
return this.directoryCache.has(path) || path.endsWith('/');
}
/**
* Check if file is a dot file
*/
isDotFile(path) {
const basename = path.split('/').pop() || '';
return basename.startsWith('.') && basename !== '.' && basename !== '..';
}
/**
* Check if file matches ignore patterns
*/
matchesIgnorePattern(file, ignorePatterns) {
return ignorePatterns.some(pattern => {
const regex = this.globToRegex(pattern);
return regex.test(file);
});
}
/**
* Make path relative to cwd
*/
makeRelative(path, cwd) {
if (path.startsWith(cwd)) {
const relative = path.slice(cwd.length);
return relative.startsWith('/') ? relative.slice(1) : relative;
}
return path;
}
/**
* Pre-load file list for a directory (for browser usage)
*/
preloadDirectory(path, files) {
this.directoryCache.set(path, files);
}
/**
* Pre-load file content (for browser usage)
*/
preloadFile(path) {
this.fileCache.set(path, []);
}
/**
* Set mock directory structure (for testing)
*/
setMockStructure(structure) {
this.directoryCache.clear();
this.fileCache.clear();
for (const [dir, files] of Object.entries(structure)) {
this.directoryCache.set(dir, files);
files.forEach(file => {
this.fileCache.set(`${dir}/${file}`, []);
});
}
}
/**
* Clear all cached data
*/
clearCache() {
this.fileCache.clear();
this.directoryCache.clear();
}
}
// Create browser glob instance
const browserGlob = new BrowserGlob();
// Export main glob function for compatibility
export const glob = browserGlob.glob.bind(browserGlob);
export const globSync = browserGlob.globSync.bind(browserGlob);
// Export utility functions
export const preloadDirectory = browserGlob.preloadDirectory.bind(browserGlob);
export const preloadFile = browserGlob.preloadFile.bind(browserGlob);
export const setMockStructure = browserGlob.setMockStructure.bind(browserGlob);
export const clearCache = browserGlob.clearCache.bind(browserGlob);
// Export default object for compatibility
export default browserGlob;
// Export the class for advanced usage
export { BrowserGlob };
//# sourceMappingURL=browser-glob.js.map