basant307/AI_Governance_Project
048
1import process from 'node:process';2import fs from 'node:fs';3import nodePath from 'node:path';4import mergeStreams from '@sindresorhus/merge-streams';5import fastGlob from 'fast-glob';6import {isDirectory, isDirectorySync} from 'path-type';7import {toPath} from 'unicorn-magic';8import {9 GITIGNORE_FILES_PATTERN,10 isIgnoredByIgnoreFiles,11 isIgnoredByIgnoreFilesSync,12} from './ignore.js';13import {isNegativePattern} from './utilities.js';14 15const assertPatternsInput = patterns => {16 if (patterns.some(pattern => typeof pattern !== 'string')) {17 throw new TypeError('Patterns must be a string or an array of strings');18 }19};20 21const normalizePathForDirectoryGlob = (filePath, cwd) => {22 const path = isNegativePattern(filePath) ? filePath.slice(1) : filePath;23 return nodePath.isAbsolute(path) ? path : nodePath.join(cwd, path);24};25 26const getDirectoryGlob = ({directoryPath, files, extensions}) => {27 const extensionGlob = extensions?.length > 0 ? `.${extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]}` : '';28 return files29 ? files.map(file => nodePath.posix.join(directoryPath, `**/${nodePath.extname(file) ? file : `${file}${extensionGlob}`}`))30 : [nodePath.posix.join(directoryPath, `**${extensionGlob ? `/*${extensionGlob}` : ''}`)];31};32 33const directoryToGlob = async (directoryPaths, {34 cwd = process.cwd(),35 files,36 extensions,37} = {}) => {38 const globs = await Promise.all(directoryPaths.map(async directoryPath =>39 (await isDirectory(normalizePathForDirectoryGlob(directoryPath, cwd))) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath),40 );41 42 return globs.flat();43};44 45const directoryToGlobSync = (directoryPaths, {46 cwd = process.cwd(),47 files,48 extensions,49} = {}) => directoryPaths.flatMap(directoryPath => isDirectorySync(normalizePathForDirectoryGlob(directoryPath, cwd)) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath);50 51const toPatternsArray = patterns => {52 patterns = [...new Set([patterns].flat())];53 assertPatternsInput(patterns);54 return patterns;55};56 57const checkCwdOption = cwd => {58 if (!cwd) {59 return;60 }61 62 let stat;63 try {64 stat = fs.statSync(cwd);65 } catch {66 return;67 }68 69 if (!stat.isDirectory()) {70 throw new Error('The `cwd` option must be a path to a directory');71 }72};73 74const normalizeOptions = (options = {}) => {75 options = {76 ...options,77 ignore: options.ignore ?? [],78 expandDirectories: options.expandDirectories ?? true,79 cwd: toPath(options.cwd),80 };81 82 checkCwdOption(options.cwd);83 84 return options;85};86 87const normalizeArguments = function_ => async (patterns, options) => function_(toPatternsArray(patterns), normalizeOptions(options));88const normalizeArgumentsSync = function_ => (patterns, options) => function_(toPatternsArray(patterns), normalizeOptions(options));89 90const getIgnoreFilesPatterns = options => {91 const {ignoreFiles, gitignore} = options;92 93 const patterns = ignoreFiles ? toPatternsArray(ignoreFiles) : [];94 if (gitignore) {95 patterns.push(GITIGNORE_FILES_PATTERN);96 }97 98 return patterns;99};100 101const getFilter = async options => {102 const ignoreFilesPatterns = getIgnoreFilesPatterns(options);103 return createFilterFunction(104 ignoreFilesPatterns.length > 0 && await isIgnoredByIgnoreFiles(ignoreFilesPatterns, options),105 );106};107 108const getFilterSync = options => {109 const ignoreFilesPatterns = getIgnoreFilesPatterns(options);110 return createFilterFunction(111 ignoreFilesPatterns.length > 0 && isIgnoredByIgnoreFilesSync(ignoreFilesPatterns, options),112 );113};114 115const createFilterFunction = isIgnored => {116 const seen = new Set();117 118 return fastGlobResult => {119 const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult);120 121 if (seen.has(pathKey) || (isIgnored && isIgnored(pathKey))) {122 return false;123 }124 125 seen.add(pathKey);126 127 return true;128 };129};130 131const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult));132 133const convertNegativePatterns = (patterns, options) => {134 const tasks = [];135 136 while (patterns.length > 0) {137 const index = patterns.findIndex(pattern => isNegativePattern(pattern));138 139 if (index === -1) {140 tasks.push({patterns, options});141 break;142 }143 144 const ignorePattern = patterns[index].slice(1);145 146 for (const task of tasks) {147 task.options.ignore.push(ignorePattern);148 }149 150 if (index !== 0) {151 tasks.push({152 patterns: patterns.slice(0, index),153 options: {154 ...options,155 ignore: [156 ...options.ignore,157 ignorePattern,158 ],159 },160 });161 }162 163 patterns = patterns.slice(index + 1);164 }165 166 return tasks;167};168 169const normalizeExpandDirectoriesOption = (options, cwd) => ({170 ...(cwd ? {cwd} : {}),171 ...(Array.isArray(options) ? {files: options} : options),172});173 174const generateTasks = async (patterns, options) => {175 const globTasks = convertNegativePatterns(patterns, options);176 177 const {cwd, expandDirectories} = options;178 179 if (!expandDirectories) {180 return globTasks;181 }182 183 const directoryToGlobOptions = normalizeExpandDirectoriesOption(expandDirectories, cwd);184 185 return Promise.all(186 globTasks.map(async task => {187 let {patterns, options} = task;188 189 [190 patterns,191 options.ignore,192 ] = await Promise.all([193 directoryToGlob(patterns, directoryToGlobOptions),194 directoryToGlob(options.ignore, {cwd}),195 ]);196 197 return {patterns, options};198 }),199 );200};201 202const generateTasksSync = (patterns, options) => {203 const globTasks = convertNegativePatterns(patterns, options);204 const {cwd, expandDirectories} = options;205 206 if (!expandDirectories) {207 return globTasks;208 }209 210 const directoryToGlobSyncOptions = normalizeExpandDirectoriesOption(expandDirectories, cwd);211 212 return globTasks.map(task => {213 let {patterns, options} = task;214 patterns = directoryToGlobSync(patterns, directoryToGlobSyncOptions);215 options.ignore = directoryToGlobSync(options.ignore, {cwd});216 return {patterns, options};217 });218};219 220export const globby = normalizeArguments(async (patterns, options) => {221 const [222 tasks,223 filter,224 ] = await Promise.all([225 generateTasks(patterns, options),226 getFilter(options),227 ]);228 229 const results = await Promise.all(tasks.map(task => fastGlob(task.patterns, task.options)));230 return unionFastGlobResults(results, filter);231});232 233export const globbySync = normalizeArgumentsSync((patterns, options) => {234 const tasks = generateTasksSync(patterns, options);235 const filter = getFilterSync(options);236 const results = tasks.map(task => fastGlob.sync(task.patterns, task.options));237 return unionFastGlobResults(results, filter);238});239 240export const globbyStream = normalizeArgumentsSync((patterns, options) => {241 const tasks = generateTasksSync(patterns, options);242 const filter = getFilterSync(options);243 const streams = tasks.map(task => fastGlob.stream(task.patterns, task.options));244 const stream = mergeStreams(streams).filter(fastGlobResult => filter(fastGlobResult));245 246 // TODO: Make it return a web stream at some point.247 // return Readable.toWeb(stream);248 249 return stream;250});251 252export const isDynamicPattern = normalizeArgumentsSync(253 (patterns, options) => patterns.some(pattern => fastGlob.isDynamicPattern(pattern, options)),254);255 256export const generateGlobTasks = normalizeArguments(generateTasks);257export const generateGlobTasksSync = normalizeArgumentsSync(generateTasksSync);258 259export {260 isGitIgnored,261 isGitIgnoredSync,262 isIgnoredByIgnoreFiles,263 isIgnoredByIgnoreFilesSync,264} from './ignore.js';265 266export const {convertPathToPattern} = fastGlob;267 