feat: use go tool
This commit is contained in:
+680
@@ -0,0 +1,680 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user