| import process from 'node:process'; |
| import fs from 'node:fs'; |
| import nodePath from 'node:path'; |
| import {Readable} from 'node:stream'; |
| import mergeStreams from '@sindresorhus/merge-streams'; |
| import fastGlob from 'fast-glob'; |
| import {toPath} from 'unicorn-magic/node'; |
| import { |
| GITIGNORE_FILES_PATTERN, |
| getIgnorePatternsAndPredicate, |
| getIgnorePatternsAndPredicateSync, |
| getGlobalGitignoreFile, |
| getGlobalGitignoreFileAsync, |
| buildGlobalMatcher, |
| } from './ignore.js'; |
| import { |
| bindFsMethod, |
| promisifyFsMethod, |
| isNegativePattern, |
| getStaticAbsolutePathPrefix, |
| normalizeNegativePattern, |
| normalizeDirectoryPatternForFastGlob, |
| adjustIgnorePatternsForParentDirectories, |
| convertPatternsForFastGlob, |
| findGitRoot, |
| findGitRootSync, |
| } from './utilities.js'; |
| |
| const assertPatternsInput = patterns => { |
| if (patterns.some(pattern => typeof pattern !== 'string')) { |
| throw new TypeError('Patterns must be a string or an array of strings'); |
| } |
| }; |
| |
| const getStatMethod = fsImplementation => { |
| if (fsImplementation) { |
| return bindFsMethod(fsImplementation.promises, 'stat') |
| ?? promisifyFsMethod(fsImplementation, 'stat'); |
| } |
| |
| return bindFsMethod(fs.promises, 'stat'); |
| }; |
| |
| const getStatSyncMethod = fsImplementation => |
| bindFsMethod(fsImplementation, 'statSync') |
| ?? bindFsMethod(fs, 'statSync'); |
| |
| const isDirectory = async (path, fsImplementation) => { |
| try { |
| const stats = await getStatMethod(fsImplementation)(path); |
| return stats.isDirectory(); |
| } catch { |
| return false; |
| } |
| }; |
| |
| const isDirectorySync = (path, fsImplementation) => { |
| try { |
| const stats = getStatSyncMethod(fsImplementation)(path); |
| return stats.isDirectory(); |
| } catch { |
| return false; |
| } |
| }; |
| |
| const normalizePathForDirectoryGlob = (filePath, cwd) => { |
| const path = isNegativePattern(filePath) ? filePath.slice(1) : filePath; |
| return nodePath.isAbsolute(path) ? path : nodePath.join(cwd, path); |
| }; |
| |
| const shouldExpandGlobstarDirectory = pattern => { |
| const match = pattern?.match(/\*\*\/([^/]+)$/); |
| if (!match) { |
| return false; |
| } |
| |
| const dirname = match[1]; |
| const hasWildcards = /[*?[\]{}]/.test(dirname); |
| const hasExtension = nodePath.extname(dirname) && !dirname.startsWith('.'); |
| |
| return !hasWildcards && !hasExtension; |
| }; |
| |
| const getDirectoryGlob = ({directoryPath, files, extensions}) => { |
| const extensionGlob = extensions?.length > 0 ? `.${extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]}` : ''; |
| return files |
| ? files.map(file => nodePath.posix.join(directoryPath, `**/${nodePath.extname(file) ? file : `${file}${extensionGlob}`}`)) |
| : [nodePath.posix.join(directoryPath, `**${extensionGlob ? `/*${extensionGlob}` : ''}`)]; |
| }; |
| |
| const directoryToGlob = async (directoryPaths, { |
| cwd = process.cwd(), |
| files, |
| extensions, |
| fs: fsImplementation, |
| } = {}) => { |
| const globs = await Promise.all(directoryPaths.map(async directoryPath => { |
| // Check pattern without negative prefix |
| const checkPattern = isNegativePattern(directoryPath) ? directoryPath.slice(1) : directoryPath; |
| |
| // Expand globstar directory patterns like **/dirname to **/dirname/** |
| if (shouldExpandGlobstarDirectory(checkPattern)) { |
| return getDirectoryGlob({directoryPath, files, extensions}); |
| } |
| |
| // Original logic for checking actual directories |
| const pathToCheck = normalizePathForDirectoryGlob(directoryPath, cwd); |
| return (await isDirectory(pathToCheck, fsImplementation)) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath; |
| })); |
| |
| return globs.flat(); |
| }; |
| |
| const directoryToGlobSync = (directoryPaths, { |
| cwd = process.cwd(), |
| files, |
| extensions, |
| fs: fsImplementation, |
| } = {}) => directoryPaths.flatMap(directoryPath => { |
| // Check pattern without negative prefix |
| const checkPattern = isNegativePattern(directoryPath) ? directoryPath.slice(1) : directoryPath; |
| |
| // Expand globstar directory patterns like **/dirname to **/dirname/** |
| if (shouldExpandGlobstarDirectory(checkPattern)) { |
| return getDirectoryGlob({directoryPath, files, extensions}); |
| } |
| |
| // Original logic for checking actual directories |
| const pathToCheck = normalizePathForDirectoryGlob(directoryPath, cwd); |
| return isDirectorySync(pathToCheck, fsImplementation) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath; |
| }); |
| |
| const toPatternsArray = patterns => { |
| patterns = [...new Set([patterns].flat())]; |
| assertPatternsInput(patterns); |
| return patterns; |
| }; |
| |
| const checkCwdOption = (cwd, fsImplementation = fs) => { |
| if (!cwd || !fsImplementation.statSync) { |
| return; |
| } |
| |
| let stats; |
| try { |
| stats = fsImplementation.statSync(cwd); |
| } catch { |
| // If stat fails (e.g., path doesn't exist), let fast-glob handle it |
| return; |
| } |
| |
| if (!stats.isDirectory()) { |
| throw new Error(`The \`cwd\` option must be a path to a directory, got: ${cwd}`); |
| } |
| }; |
| |
| const normalizeOptions = (options = {}) => { |
| // Normalize ignore to an array (fast-glob accepts string but we need array internally) |
| const ignore = options.ignore |
| ? (Array.isArray(options.ignore) ? options.ignore : [options.ignore]) |
| : []; |
| |
| options = { |
| ...options, |
| ignore, |
| expandDirectories: options.expandDirectories ?? true, |
| cwd: toPath(options.cwd), |
| }; |
| |
| checkCwdOption(options.cwd, options.fs); |
| |
| return options; |
| }; |
| |
| const normalizeArguments = function_ => async (patterns, options) => function_(toPatternsArray(patterns), normalizeOptions(options)); |
| const normalizeArgumentsSync = function_ => (patterns, options) => function_(toPatternsArray(patterns), normalizeOptions(options)); |
| |
| const getIgnoreFilesPatterns = options => { |
| const {ignoreFiles, gitignore} = options; |
| |
| const patterns = ignoreFiles ? toPatternsArray(ignoreFiles) : []; |
| if (gitignore) { |
| patterns.push(GITIGNORE_FILES_PATTERN); |
| } |
| |
| return patterns; |
| }; |
| |
| const isPathIgnored = (matcher, globalMatcher, path) => { |
| const globalResult = globalMatcher ? globalMatcher(path) : undefined; |
| const result = matcher ? matcher(path) : undefined; |
| |
| if (result?.unignored) { |
| return false; |
| } |
| |
| return Boolean(result?.ignored || globalResult?.ignored); |
| }; |
| |
| const hasIgnoredAncestorDirectory = (matcher, globalMatcher, file) => { |
| let currentPath = file; |
| |
| while (true) { |
| const parentDirectory = nodePath.dirname(currentPath); |
| if (parentDirectory === currentPath) { |
| return false; |
| } |
| |
| if (isPathIgnored(matcher, globalMatcher, `${parentDirectory}${nodePath.sep}`)) { |
| return true; |
| } |
| |
| currentPath = parentDirectory; |
| } |
| }; |
| |
| const combinePredicate = (matcher, globalMatcher) => { |
| if (!matcher && !globalMatcher) { |
| return false; |
| } |
| |
| return file => { |
| const result = matcher ? matcher(file) : undefined; |
| |
| // A local negation (e.g. `!file`) re-includes the file, unless |
| // a parent directory is ignored by either matcher. |
| if (result?.unignored) { |
| const globalResult = globalMatcher ? globalMatcher(file) : undefined; |
| return globalResult?.ignored && hasIgnoredAncestorDirectory(matcher, globalMatcher, file); |
| } |
| |
| return isPathIgnored(matcher, globalMatcher, file); |
| }; |
| }; |
| |
| const buildIgnoreFilterResult = (options, cwd, {patterns, matcher, usingGitRoot}, globalMatcher, createFilter) => { |
| const finalPredicate = combinePredicate(matcher, globalMatcher); |
| |
| // Convert patterns to fast-glob format (may return empty array if predicate should handle everything) |
| const patternsForFastGlob = convertPatternsForFastGlob(patterns, usingGitRoot, normalizeDirectoryPatternForFastGlob); |
| |
| return { |
| options: { |
| ...options, |
| ignore: [...options.ignore, ...patternsForFastGlob], |
| }, |
| filter: createFilter(finalPredicate, cwd, options.fs), |
| }; |
| }; |
| |
| /** |
| Apply gitignore patterns to options and return filter predicate. |
| |
| When negation patterns are present (e.g., '!important.log'), we cannot pass positive patterns to fast-glob because it would filter out files before our predicate can re-include them. In this case, we rely entirely on the predicate for filtering, which handles negations correctly. |
| |
| When there are no negations, we optimize by passing patterns to fast-glob's ignore option to skip directories during traversal (performance optimization). |
| |
| All patterns (including negated) are always used in the filter predicate to ensure correct Git-compatible behavior. |
| |
| @returns {Promise<{options: Object, filter: Function}>} |
| */ |
| const applyIgnoreFilesAndGetFilter = async options => { |
| const cwd = options.cwd ?? process.cwd(); |
| const ignoreFilesPatterns = getIgnoreFilesPatterns(options); |
| const globalIgnoreFile = options.globalGitignore ? await getGlobalGitignoreFileAsync(options) : undefined; |
| |
| if (ignoreFilesPatterns.length === 0 && !globalIgnoreFile) { |
| return { |
| options, |
| filter: createFilterFunctionAsync(false, cwd, options.fs), |
| }; |
| } |
| |
| // Read ignore files once and get both patterns and predicate |
| // Enable parent .gitignore search when using gitignore option |
| const includeParentIgnoreFiles = options.gitignore === true; |
| const ignoreResult = ignoreFilesPatterns.length > 0 |
| ? await getIgnorePatternsAndPredicate(ignoreFilesPatterns, options, includeParentIgnoreFiles) |
| : {patterns: [], matcher: false, usingGitRoot: false}; |
| |
| const globalGitRoot = globalIgnoreFile ? await findGitRoot(cwd, options.fs) : undefined; |
| const globalMatcher = globalIgnoreFile ? buildGlobalMatcher(globalIgnoreFile, cwd, globalGitRoot ?? cwd) : undefined; |
| |
| return buildIgnoreFilterResult(options, cwd, ignoreResult, globalMatcher, createFilterFunctionAsync); |
| }; |
| |
| /** |
| Apply gitignore patterns to options and return filter predicate (sync version). |
| |
| @returns {{options: Object, filter: Function}} |
| */ |
| const applyIgnoreFilesAndGetFilterSync = options => { |
| const cwd = options.cwd ?? process.cwd(); |
| const ignoreFilesPatterns = getIgnoreFilesPatterns(options); |
| const globalIgnoreFile = options.globalGitignore ? getGlobalGitignoreFile(options) : undefined; |
| |
| if (ignoreFilesPatterns.length === 0 && !globalIgnoreFile) { |
| return { |
| options, |
| filter: createFilterFunction(false, cwd, options.fs), |
| }; |
| } |
| |
| // Read ignore files once and get both patterns and predicate |
| // Enable parent .gitignore search when using gitignore option |
| const includeParentIgnoreFiles = options.gitignore === true; |
| const ignoreResult = ignoreFilesPatterns.length > 0 |
| ? getIgnorePatternsAndPredicateSync(ignoreFilesPatterns, options, includeParentIgnoreFiles) |
| : {patterns: [], matcher: false, usingGitRoot: false}; |
| |
| const globalGitRoot = globalIgnoreFile ? findGitRootSync(cwd, options.fs) : undefined; |
| const globalMatcher = globalIgnoreFile ? buildGlobalMatcher(globalIgnoreFile, cwd, globalGitRoot ?? cwd) : undefined; |
| |
| return buildIgnoreFilterResult(options, cwd, ignoreResult, globalMatcher, createFilterFunction); |
| }; |
| |
| const assertGlobalGitignoreSyncSupport = options => { |
| if (options.globalGitignore && options.fs && !options.fs.statSync) { |
| throw new Error('The `globalGitignore` option in `globbySync()` requires `fs.statSync` when a custom `fs` is provided.'); |
| } |
| }; |
| |
| const globalGitignoreAsyncStatErrorMessage = 'The `globalGitignore` option in `globby()` and `globbyStream()` requires `fs.promises.stat` or `fs.stat` when a custom `fs` is provided.'; |
| |
| const assertGlobalGitignoreAsyncSupport = options => { |
| if (!options.globalGitignore || !options.fs) { |
| return; |
| } |
| |
| if (!options.fs.promises?.stat && !options.fs.stat) { |
| throw new Error(globalGitignoreAsyncStatErrorMessage); |
| } |
| }; |
| |
| const createPathResolver = cwd => { |
| const basePath = cwd || process.cwd(); |
| const pathCache = new Map(); |
| |
| return pathKey => { |
| let absolutePath = pathCache.get(pathKey); |
| if (absolutePath === undefined) { |
| if (pathCache.size > 10_000) { |
| pathCache.clear(); |
| } |
| |
| absolutePath = nodePath.isAbsolute(pathKey) ? pathKey : nodePath.resolve(basePath, pathKey); |
| pathCache.set(pathKey, absolutePath); |
| } |
| |
| return absolutePath; |
| }; |
| }; |
| |
| const createAsyncDirectoryCheck = fsMethod => { |
| const directoryCache = new Map(); |
| |
| return async absolutePath => { |
| let isDirectory = directoryCache.get(absolutePath); |
| if (isDirectory !== undefined) { |
| return isDirectory; |
| } |
| |
| try { |
| const stats = await fsMethod?.(absolutePath); |
| isDirectory = Boolean(stats?.isDirectory()); |
| } catch { |
| isDirectory = false; |
| } |
| |
| if (directoryCache.size > 10_000) { |
| directoryCache.clear(); |
| } |
| |
| directoryCache.set(absolutePath, isDirectory); |
| return isDirectory; |
| }; |
| }; |
| |
| const createDirectoryCheck = fsMethod => { |
| const directoryCache = new Map(); |
| |
| return absolutePath => { |
| let isDirectory = directoryCache.get(absolutePath); |
| if (isDirectory !== undefined) { |
| return isDirectory; |
| } |
| |
| try { |
| isDirectory = Boolean(fsMethod?.(absolutePath)?.isDirectory()); |
| } catch { |
| isDirectory = false; |
| } |
| |
| if (directoryCache.size > 10_000) { |
| directoryCache.clear(); |
| } |
| |
| directoryCache.set(absolutePath, isDirectory); |
| return isDirectory; |
| }; |
| }; |
| |
| const createFilterFunctionAsync = (isIgnored, cwd, fsImplementation) => { |
| const resolveAbsolutePath = createPathResolver(cwd); |
| const isDirectoryEntry = createAsyncDirectoryCheck(getStatMethod(fsImplementation)); |
| |
| return async fastGlobResult => { |
| if (!isIgnored) { |
| return true; |
| } |
| |
| const absolutePath = resolveAbsolutePath(nodePath.normalize(fastGlobResult.path ?? fastGlobResult)); |
| if (isIgnored(absolutePath)) { |
| return false; |
| } |
| |
| return !(await isDirectoryEntry(absolutePath) && isIgnored(`${absolutePath}${nodePath.sep}`)); |
| }; |
| }; |
| |
| const createFilterFunction = (isIgnored, cwd, fsImplementation) => { |
| const seen = new Set(); |
| const resolveAbsolutePath = createPathResolver(cwd); |
| const isDirectoryEntry = createDirectoryCheck(getStatSyncMethod(fsImplementation)); |
| |
| return fastGlobResult => { |
| const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult); |
| |
| if (seen.has(pathKey)) { |
| return false; |
| } |
| |
| if (isIgnored) { |
| const absolutePath = resolveAbsolutePath(pathKey); |
| if (isIgnored(absolutePath)) { |
| return false; |
| } |
| |
| if (isDirectoryEntry(absolutePath) && isIgnored(`${absolutePath}${nodePath.sep}`)) { |
| return false; |
| } |
| } |
| |
| seen.add(pathKey); |
| return true; |
| }; |
| }; |
| |
| const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult)); |
| const unionFastGlobResultsAsync = async (results, filter) => { |
| results = results.flat(); |
| const matches = await Promise.all(results.map(fastGlobResult => filter(fastGlobResult))); |
| const seen = new Set(); |
| |
| return results.filter((fastGlobResult, index) => { |
| if (!matches[index]) { |
| return false; |
| } |
| |
| const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult); |
| if (seen.has(pathKey)) { |
| return false; |
| } |
| |
| seen.add(pathKey); |
| return true; |
| }); |
| }; |
| |
| const convertNegativePatterns = (patterns, options) => { |
| // If all patterns are negative and expandNegationOnlyPatterns is enabled (default), |
| // prepend a positive catch-all pattern to make negation-only patterns work intuitively |
| // (e.g., '!*.json' matches all files except JSON) |
| if (patterns.length > 0 && patterns.every(pattern => isNegativePattern(pattern))) { |
| if (options.expandNegationOnlyPatterns === false) { |
| return []; |
| } |
| |
| patterns = ['**/*', ...patterns]; |
| } |
| |
| const positiveAbsolutePathPrefixes = []; |
| let hasRelativePositivePattern = false; |
| const normalizedPatterns = []; |
| |
| for (const pattern of patterns) { |
| if (isNegativePattern(pattern)) { |
| normalizedPatterns.push(`!${normalizeNegativePattern(pattern.slice(1), positiveAbsolutePathPrefixes, hasRelativePositivePattern)}`); |
| continue; |
| } |
| |
| normalizedPatterns.push(pattern); |
| |
| const staticAbsolutePathPrefix = getStaticAbsolutePathPrefix(pattern); |
| if (staticAbsolutePathPrefix === undefined) { |
| hasRelativePositivePattern = true; |
| continue; |
| } |
| |
| positiveAbsolutePathPrefixes.push(staticAbsolutePathPrefix); |
| } |
| |
| patterns = normalizedPatterns; |
| |
| const tasks = []; |
| |
| while (patterns.length > 0) { |
| const index = patterns.findIndex(pattern => isNegativePattern(pattern)); |
| |
| if (index === -1) { |
| tasks.push({patterns, options}); |
| break; |
| } |
| |
| const ignorePattern = patterns[index].slice(1); |
| |
| for (const task of tasks) { |
| task.options.ignore.push(ignorePattern); |
| } |
| |
| if (index !== 0) { |
| tasks.push({ |
| patterns: patterns.slice(0, index), |
| options: { |
| ...options, |
| ignore: [ |
| ...options.ignore, |
| ignorePattern, |
| ], |
| }, |
| }); |
| } |
| |
| patterns = patterns.slice(index + 1); |
| } |
| |
| return tasks; |
| }; |
| |
| const applyParentDirectoryIgnoreAdjustments = tasks => tasks.map(task => ({ |
| patterns: task.patterns, |
| options: { |
| ...task.options, |
| ignore: adjustIgnorePatternsForParentDirectories(task.patterns, task.options.ignore), |
| }, |
| })); |
| |
| const normalizeExpandDirectoriesOption = (options, cwd) => ({ |
| ...(cwd ? {cwd} : {}), |
| ...(Array.isArray(options) ? {files: options} : options), |
| }); |
| |
| const generateTasks = async (patterns, options) => { |
| const globTasks = convertNegativePatterns(patterns, options); |
| |
| const {cwd, expandDirectories, fs: fsImplementation} = options; |
| |
| if (!expandDirectories) { |
| return applyParentDirectoryIgnoreAdjustments(globTasks); |
| } |
| |
| const directoryToGlobOptions = { |
| ...normalizeExpandDirectoriesOption(expandDirectories, cwd), |
| fs: fsImplementation, |
| }; |
| |
| return Promise.all(globTasks.map(async task => { |
| let {patterns, options} = task; |
| |
| [ |
| patterns, |
| options.ignore, |
| ] = await Promise.all([ |
| directoryToGlob(patterns, directoryToGlobOptions), |
| directoryToGlob(options.ignore, {cwd, fs: fsImplementation}), |
| ]); |
| |
| // Adjust ignore patterns for parent directory references |
| options.ignore = adjustIgnorePatternsForParentDirectories(patterns, options.ignore); |
| |
| return {patterns, options}; |
| })); |
| }; |
| |
| const generateTasksSync = (patterns, options) => { |
| const globTasks = convertNegativePatterns(patterns, options); |
| const {cwd, expandDirectories, fs: fsImplementation} = options; |
| |
| if (!expandDirectories) { |
| return applyParentDirectoryIgnoreAdjustments(globTasks); |
| } |
| |
| const directoryToGlobSyncOptions = { |
| ...normalizeExpandDirectoriesOption(expandDirectories, cwd), |
| fs: fsImplementation, |
| }; |
| |
| return globTasks.map(task => { |
| let {patterns, options} = task; |
| patterns = directoryToGlobSync(patterns, directoryToGlobSyncOptions); |
| options.ignore = directoryToGlobSync(options.ignore, {cwd, fs: fsImplementation}); |
| |
| // Adjust ignore patterns for parent directory references |
| options.ignore = adjustIgnorePatternsForParentDirectories(patterns, options.ignore); |
| |
| return {patterns, options}; |
| }); |
| }; |
| |
| export const globby = normalizeArguments(async (patterns, options) => { |
| assertGlobalGitignoreAsyncSupport(options); |
| |
| // Apply ignore files and get filter (reads .gitignore files once) |
| const {options: modifiedOptions, filter} = await applyIgnoreFilesAndGetFilter(options); |
| |
| // Generate tasks with modified options (includes gitignore patterns in ignore option) |
| const tasks = await generateTasks(patterns, modifiedOptions); |
| |
| const results = await Promise.all(tasks.map(task => fastGlob(task.patterns, task.options))); |
| return unionFastGlobResultsAsync(results, filter); |
| }); |
| |
| export const globbySync = normalizeArgumentsSync((patterns, options) => { |
| assertGlobalGitignoreSyncSupport(options); |
| |
| // Apply ignore files and get filter (reads .gitignore files once) |
| const {options: modifiedOptions, filter} = applyIgnoreFilesAndGetFilterSync(options); |
| |
| // Generate tasks with modified options (includes gitignore patterns in ignore option) |
| const tasks = generateTasksSync(patterns, modifiedOptions); |
| |
| const results = tasks.map(task => fastGlob.sync(task.patterns, task.options)); |
| return unionFastGlobResults(results, filter); |
| }); |
| |
| export const globbyStream = normalizeArgumentsSync((patterns, options) => { |
| assertGlobalGitignoreAsyncSupport(options); |
| |
| const seen = new Set(); |
| const stream = Readable.from((async function * () { |
| // Apply ignore files and get filter (reads .gitignore files once) |
| const {options: modifiedOptions, filter} = await applyIgnoreFilesAndGetFilter(options); |
| |
| // Generate tasks with modified options (includes gitignore patterns in ignore option) |
| const tasks = await generateTasks(patterns, modifiedOptions); |
| |
| if (tasks.length === 0) { |
| return; |
| } |
| |
| const streams = tasks.map(task => fastGlob.stream(task.patterns, task.options)); |
| |
| for await (const fastGlobResult of mergeStreams(streams)) { |
| const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult); |
| if (!seen.has(pathKey) && await filter(fastGlobResult)) { |
| seen.add(pathKey); |
| yield fastGlobResult; |
| } |
| } |
| })()); |
| |
| // Returning a web stream will require revisiting once Readable.toWeb integration is viable. |
| // return Readable.toWeb(stream); |
| |
| return stream; |
| }); |
| |
| export const isDynamicPattern = normalizeArgumentsSync((patterns, options) => patterns.some(pattern => fastGlob.isDynamicPattern(pattern, options))); |
| |
| export const generateGlobTasks = normalizeArguments(generateTasks); |
| export const generateGlobTasksSync = normalizeArgumentsSync(generateTasksSync); |
| |
| export { |
| isGitIgnored, |
| isGitIgnoredSync, |
| isIgnoredByIgnoreFiles, |
| isIgnoredByIgnoreFilesSync, |
| } from './ignore.js'; |
| |
| export const {convertPathToPattern} = fastGlob; |