feat: init

This commit is contained in:
2026-02-13 22:02:30 +01:00
commit 8f9ff830fb
16711 changed files with 3307340 additions and 0 deletions

337
node_modules/globby/ignore.js generated vendored Normal file
View File

@@ -0,0 +1,337 @@
import process from 'node:process';
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import path from 'node:path';
import fastGlob from 'fast-glob';
import gitIgnore from 'ignore';
import isPathInside from 'is-path-inside';
import slash from 'slash';
import {toPath} from 'unicorn-magic/node';
import {
isNegativePattern,
bindFsMethod,
promisifyFsMethod,
findGitRoot,
findGitRootSync,
getParentGitignorePaths,
} from './utilities.js';
const defaultIgnoredDirectories = [
'**/node_modules',
'**/flow-typed',
'**/coverage',
'**/.git',
];
const ignoreFilesGlobOptions = {
absolute: true,
dot: true,
};
export const GITIGNORE_FILES_PATTERN = '**/.gitignore';
const getReadFileMethod = fsImplementation =>
bindFsMethod(fsImplementation?.promises, 'readFile')
?? bindFsMethod(fsPromises, 'readFile')
?? promisifyFsMethod(fsImplementation, 'readFile');
const getReadFileSyncMethod = fsImplementation =>
bindFsMethod(fsImplementation, 'readFileSync')
?? bindFsMethod(fs, 'readFileSync');
const shouldSkipIgnoreFileError = (error, suppressErrors) => {
if (!error) {
return Boolean(suppressErrors);
}
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
return true;
}
return Boolean(suppressErrors);
};
const createIgnoreFileReadError = (filePath, error) => {
if (error instanceof Error) {
error.message = `Failed to read ignore file at ${filePath}: ${error.message}`;
return error;
}
return new Error(`Failed to read ignore file at ${filePath}: ${String(error)}`);
};
const processIgnoreFileCore = (filePath, readMethod, suppressErrors) => {
try {
const content = readMethod(filePath, 'utf8');
return {filePath, content};
} catch (error) {
if (shouldSkipIgnoreFileError(error, suppressErrors)) {
return undefined;
}
throw createIgnoreFileReadError(filePath, error);
}
};
const readIgnoreFilesSafely = async (paths, readFileMethod, suppressErrors) => {
const fileResults = await Promise.all(paths.map(async filePath => {
try {
const content = await readFileMethod(filePath, 'utf8');
return {filePath, content};
} catch (error) {
if (shouldSkipIgnoreFileError(error, suppressErrors)) {
return undefined;
}
throw createIgnoreFileReadError(filePath, error);
}
}));
return fileResults.filter(Boolean);
};
const readIgnoreFilesSafelySync = (paths, readFileSyncMethod, suppressErrors) => paths
.map(filePath => processIgnoreFileCore(filePath, readFileSyncMethod, suppressErrors))
.filter(Boolean);
const dedupePaths = paths => {
const seen = new Set();
return paths.filter(filePath => {
if (seen.has(filePath)) {
return false;
}
seen.add(filePath);
return true;
});
};
const globIgnoreFiles = (globFunction, patterns, normalizedOptions) => globFunction(patterns, {
...normalizedOptions,
...ignoreFilesGlobOptions, // Must be last to ensure absolute/dot flags stick
});
const getParentIgnorePaths = (gitRoot, normalizedOptions) => gitRoot
? getParentGitignorePaths(gitRoot, normalizedOptions.cwd)
: [];
const combineIgnoreFilePaths = (gitRoot, normalizedOptions, childPaths) => dedupePaths([
...getParentIgnorePaths(gitRoot, normalizedOptions),
...childPaths,
]);
const buildIgnoreResult = (files, normalizedOptions, gitRoot) => {
const baseDir = gitRoot || normalizedOptions.cwd;
const patterns = getPatternsFromIgnoreFiles(files, baseDir);
return {
patterns,
predicate: createIgnorePredicate(patterns, normalizedOptions.cwd, baseDir),
usingGitRoot: Boolean(gitRoot && gitRoot !== normalizedOptions.cwd),
};
};
// Apply base path to gitignore patterns based on .gitignore spec 2.22.1
// https://git-scm.com/docs/gitignore#_pattern_format
// See also https://github.com/sindresorhus/globby/issues/146
const applyBaseToPattern = (pattern, base) => {
if (!base) {
return pattern;
}
const isNegative = isNegativePattern(pattern);
const cleanPattern = isNegative ? pattern.slice(1) : pattern;
// Check if pattern has non-trailing slashes
const slashIndex = cleanPattern.indexOf('/');
const hasNonTrailingSlash = slashIndex !== -1 && slashIndex !== cleanPattern.length - 1;
let result;
if (!hasNonTrailingSlash) {
// "If there is no separator at the beginning or middle of the pattern,
// then the pattern may also match at any level below the .gitignore level."
// So patterns like '*.log' or 'temp' or 'build/' (trailing slash) match recursively.
result = path.posix.join(base, '**', cleanPattern);
} else if (cleanPattern.startsWith('/')) {
// "If there is a separator at the beginning [...] of the pattern,
// then the pattern is relative to the directory level of the particular .gitignore file itself."
// Leading slash anchors the pattern to the .gitignore's directory.
result = path.posix.join(base, cleanPattern.slice(1));
} else {
// "If there is a separator [...] middle [...] of the pattern,
// then the pattern is relative to the directory level of the particular .gitignore file itself."
// Patterns like 'src/foo' are relative to the .gitignore's directory.
result = path.posix.join(base, cleanPattern);
}
return isNegative ? '!' + result : result;
};
const parseIgnoreFile = (file, cwd) => {
const base = slash(path.relative(cwd, path.dirname(file.filePath)));
return file.content
.split(/\r?\n/)
.filter(line => line && !line.startsWith('#'))
.map(pattern => applyBaseToPattern(pattern, base));
};
const toRelativePath = (fileOrDirectory, cwd) => {
if (path.isAbsolute(fileOrDirectory)) {
// When paths are equal, path.relative returns empty string which is valid
// isPathInside returns false for equal paths, so check this case first
const relativePath = path.relative(cwd, fileOrDirectory);
if (relativePath && !isPathInside(fileOrDirectory, cwd)) {
// Path is outside cwd - it cannot be ignored by patterns in cwd
// Return undefined to indicate this path is outside scope
return undefined;
}
return relativePath;
}
// Normalize relative paths:
// - Git treats './foo' as 'foo' when checking against patterns
// - Patterns starting with './' in .gitignore are invalid and don't match anything
// - The ignore library expects normalized paths without './' prefix
if (fileOrDirectory.startsWith('./')) {
return fileOrDirectory.slice(2);
}
// Paths with ../ point outside cwd and cannot match patterns from this directory
// Return undefined to indicate this path is outside scope
if (fileOrDirectory.startsWith('../')) {
return undefined;
}
return fileOrDirectory;
};
const createIgnorePredicate = (patterns, cwd, baseDir) => {
const ignores = gitIgnore().add(patterns);
// Normalize to handle path separator and . / .. components consistently
const resolvedCwd = path.normalize(path.resolve(cwd));
const resolvedBaseDir = path.normalize(path.resolve(baseDir));
return fileOrDirectory => {
fileOrDirectory = toPath(fileOrDirectory);
// Never ignore the cwd itself - use normalized comparison
const normalizedPath = path.normalize(path.resolve(fileOrDirectory));
if (normalizedPath === resolvedCwd) {
return false;
}
// Convert to relative path from baseDir (use normalized baseDir)
const relativePath = toRelativePath(fileOrDirectory, resolvedBaseDir);
// If path is outside baseDir (undefined), it can't be ignored by patterns
if (relativePath === undefined) {
return false;
}
return relativePath ? ignores.ignores(slash(relativePath)) : false;
};
};
const normalizeOptions = (options = {}) => {
const ignoreOption = options.ignore
? (Array.isArray(options.ignore) ? options.ignore : [options.ignore])
: [];
const cwd = toPath(options.cwd) ?? process.cwd();
// Adjust deep option for fast-glob: fast-glob's deep counts differently than expected
// User's deep: 0 = root only -> fast-glob needs: 1
// User's deep: 1 = root + 1 level -> fast-glob needs: 2
const deep = typeof options.deep === 'number' ? Math.max(0, options.deep) + 1 : Number.POSITIVE_INFINITY;
// Only pass through specific fast-glob options that make sense for finding ignore files
return {
cwd,
suppressErrors: options.suppressErrors ?? false,
deep,
ignore: [...ignoreOption, ...defaultIgnoredDirectories],
followSymbolicLinks: options.followSymbolicLinks ?? true,
concurrency: options.concurrency,
throwErrorOnBrokenSymbolicLink: options.throwErrorOnBrokenSymbolicLink ?? false,
fs: options.fs,
};
};
const collectIgnoreFileArtifactsAsync = async (patterns, options, includeParentIgnoreFiles) => {
const normalizedOptions = normalizeOptions(options);
const childPaths = await globIgnoreFiles(fastGlob, patterns, normalizedOptions);
const gitRoot = includeParentIgnoreFiles
? await findGitRoot(normalizedOptions.cwd, normalizedOptions.fs)
: undefined;
const allPaths = combineIgnoreFilePaths(gitRoot, normalizedOptions, childPaths);
const readFileMethod = getReadFileMethod(normalizedOptions.fs);
const files = await readIgnoreFilesSafely(allPaths, readFileMethod, normalizedOptions.suppressErrors);
return {files, normalizedOptions, gitRoot};
};
const collectIgnoreFileArtifactsSync = (patterns, options, includeParentIgnoreFiles) => {
const normalizedOptions = normalizeOptions(options);
const childPaths = globIgnoreFiles(fastGlob.sync, patterns, normalizedOptions);
const gitRoot = includeParentIgnoreFiles
? findGitRootSync(normalizedOptions.cwd, normalizedOptions.fs)
: undefined;
const allPaths = combineIgnoreFilePaths(gitRoot, normalizedOptions, childPaths);
const readFileSyncMethod = getReadFileSyncMethod(normalizedOptions.fs);
const files = readIgnoreFilesSafelySync(allPaths, readFileSyncMethod, normalizedOptions.suppressErrors);
return {files, normalizedOptions, gitRoot};
};
export const isIgnoredByIgnoreFiles = async (patterns, options) => {
const {files, normalizedOptions, gitRoot} = await collectIgnoreFileArtifactsAsync(patterns, options, false);
return buildIgnoreResult(files, normalizedOptions, gitRoot).predicate;
};
export const isIgnoredByIgnoreFilesSync = (patterns, options) => {
const {files, normalizedOptions, gitRoot} = collectIgnoreFileArtifactsSync(patterns, options, false);
return buildIgnoreResult(files, normalizedOptions, gitRoot).predicate;
};
const getPatternsFromIgnoreFiles = (files, baseDir) => files.flatMap(file => parseIgnoreFile(file, baseDir));
/**
Read ignore files and return both patterns and predicate.
This avoids reading the same files twice (once for patterns, once for filtering).
@param {string[]} patterns - Patterns to find ignore files
@param {Object} options - Options object
@param {boolean} [includeParentIgnoreFiles=false] - Whether to search for parent .gitignore files
@returns {Promise<{patterns: string[], predicate: Function, usingGitRoot: boolean}>}
*/
export const getIgnorePatternsAndPredicate = async (patterns, options, includeParentIgnoreFiles = false) => {
const {files, normalizedOptions, gitRoot} = await collectIgnoreFileArtifactsAsync(
patterns,
options,
includeParentIgnoreFiles,
);
return buildIgnoreResult(files, normalizedOptions, gitRoot);
};
/**
Read ignore files and return both patterns and predicate (sync version).
@param {string[]} patterns - Patterns to find ignore files
@param {Object} options - Options object
@param {boolean} [includeParentIgnoreFiles=false] - Whether to search for parent .gitignore files
@returns {{patterns: string[], predicate: Function, usingGitRoot: boolean}}
*/
export const getIgnorePatternsAndPredicateSync = (patterns, options, includeParentIgnoreFiles = false) => {
const {files, normalizedOptions, gitRoot} = collectIgnoreFileArtifactsSync(
patterns,
options,
includeParentIgnoreFiles,
);
return buildIgnoreResult(files, normalizedOptions, gitRoot);
};
export const isGitIgnored = options => isIgnoredByIgnoreFiles(GITIGNORE_FILES_PATTERN, options);
export const isGitIgnoredSync = options => isIgnoredByIgnoreFilesSync(GITIGNORE_FILES_PATTERN, options);

394
node_modules/globby/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,394 @@
import type FastGlob from 'fast-glob';
export type GlobEntry = FastGlob.Entry;
export type GlobTask = {
readonly patterns: string[];
readonly options: Options;
};
export type ExpandDirectoriesOption =
| boolean
| readonly string[]
| {files?: readonly string[]; extensions?: readonly string[]};
type FastGlobOptionsWithoutCwd = Omit<FastGlob.Options, 'cwd'>;
export type Options = {
/**
If set to `true`, `globby` will automatically glob directories for you. If you define an `Array` it will only glob files that matches the patterns inside the `Array`. You can also define an `Object` with `files` and `extensions` like in the example below.
Note that if you set this option to `false`, you won't get back matched directories unless you set `onlyFiles: false`.
@default true
@example
```
import {globby} from 'globby';
const paths = await globby('images', {
expandDirectories: {
files: ['cat', 'unicorn', '*.jpg'],
extensions: ['png']
}
});
console.log(paths);
//=> ['cat.png', 'unicorn.png', 'cow.jpg', 'rainbow.jpg']
```
*/
readonly expandDirectories?: ExpandDirectoriesOption;
/**
Respect ignore patterns in `.gitignore` files that apply to the globbed files.
When enabled, globby searches for `.gitignore` files from the current working directory downward, and if a Git repository is detected (by finding a `.git` directory), it also respects `.gitignore` files in parent directories up to the repository root. This matches Git's actual behavior where patterns from parent `.gitignore` files apply to subdirectories.
Gitignore patterns take priority over user patterns, matching Git's behavior. To include gitignored files, set this to `false`.
Performance: Globby reads `.gitignore` files before globbing. When there are no negation patterns (like `!important.log`) and no parent `.gitignore` files are found, it passes ignore patterns to fast-glob to skip traversing ignored directories entirely, which significantly improves performance for large `node_modules` or build directories. When negation patterns or parent `.gitignore` files are present, all filtering is done after traversal to ensure correct Git-compatible behavior. For optimal performance, prefer specific `.gitignore` patterns without negations, or use `ignoreFiles: '.gitignore'` to target only the root ignore file.
@default false
*/
readonly gitignore?: boolean;
/**
Glob patterns to look for ignore files, which are then used to ignore globbed files.
This is a more generic form of the `gitignore` option, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
Performance tip: Using a specific path like `'.gitignore'` is much faster than recursive patterns.
@default undefined
*/
readonly ignoreFiles?: string | readonly string[];
/**
When only negation patterns are provided (e.g., `['!*.json']`), automatically prepend a catch-all pattern (`**\/*`) to match all files before applying negations.
Set to `false` to return an empty array when only negation patterns are provided. This can be useful when patterns are user-controlled, to avoid unexpectedly matching all files.
@default true
@example
```
import {globby} from 'globby';
// Default behavior: matches all files except .json
await globby(['!*.json']);
//=> ['file.txt', 'image.png', ...]
// Disable expansion: returns empty array
await globby(['!*.json'], {expandNegationOnlyPatterns: false});
//=> []
```
*/
readonly expandNegationOnlyPatterns?: boolean;
/**
The current working directory in which to search.
@default process.cwd()
*/
readonly cwd?: URL | string;
} & FastGlobOptionsWithoutCwd;
export type GitignoreOptions = {
/**
The current working directory in which to search.
@default process.cwd()
*/
readonly cwd?: URL | string;
/**
Suppress errors when encountering directories or files without read permissions.
By default, fast-glob only suppresses `ENOENT` errors. Set to `true` to suppress any error.
@default false
*/
readonly suppressErrors?: boolean;
/**
Specifies the maximum depth of ignore file search relative to the start directory.
@default Infinity
*/
readonly deep?: number;
/**
Glob patterns to exclude from ignore file search.
@default []
*/
readonly ignore?: string | readonly string[];
/**
Indicates whether to traverse descendants of symbolic link directories.
@default true
*/
readonly followSymbolicLinks?: boolean;
/**
Specifies the maximum number of concurrent requests from a reader to read directories.
@default os.cpus().length
*/
readonly concurrency?: number;
/**
Throw an error when symbolic link is broken if `true` or safely return `lstat` call if `false`.
@default false
*/
readonly throwErrorOnBrokenSymbolicLink?: boolean;
/**
Custom file system implementation (useful for testing or virtual file systems).
@default undefined
*/
readonly fs?: FastGlob.Options['fs'];
};
export type GlobbyFilterFunction = (path: URL | string) => boolean;
type AsyncIterableReadable<Value> = Omit<NodeJS.ReadableStream, typeof Symbol.asyncIterator> & {
[Symbol.asyncIterator](): NodeJS.AsyncIterator<Value>;
};
/**
A readable stream that yields string paths from glob patterns.
*/
export type GlobbyStream = AsyncIterableReadable<string>;
/**
A readable stream that yields `GlobEntry` objects from glob patterns when `objectMode` is enabled.
*/
export type GlobbyEntryStream = AsyncIterableReadable<GlobEntry>;
/**
Find files and directories using glob patterns.
Note that glob patterns can only contain forward-slashes, not backward-slashes, so if you want to construct a glob pattern from path components, you need to use `path.posix.join()` instead of `path.join()`.
Windows: Patterns with backslashes will silently fail. Use `path.posix.join()` or `convertPathToPattern()`.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns). Supports negation patterns to exclude files. When using only negation patterns (like `['!*.json']`), globby implicitly prepends a catch-all pattern to match all files before applying negations.
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns The matching paths.
@example
```
import {globby} from 'globby';
const paths = await globby(['*', '!cake']);
console.log(paths);
//=> ['unicorn', 'rainbow']
```
@example
```
import {globby} from 'globby';
// Negation-only patterns match all files except the negated ones
const paths = await globby(['!*.json', '!*.xml'], {cwd: 'config'});
console.log(paths);
//=> ['config.js', 'settings.yaml']
```
*/
export function globby(
patterns: string | readonly string[],
options: Options & ({objectMode: true} | {stats: true})
): Promise<GlobEntry[]>;
export function globby(
patterns: string | readonly string[],
options?: Options
): Promise<string[]>;
/**
Find files and directories using glob patterns.
Note that glob patterns can only contain forward-slashes, not backward-slashes, so if you want to construct a glob pattern from path components, you need to use `path.posix.join()` instead of `path.join()`.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns The matching paths.
*/
export function globbySync(
patterns: string | readonly string[],
options: Options & ({objectMode: true} | {stats: true})
): GlobEntry[];
export function globbySync(
patterns: string | readonly string[],
options?: Options
): string[];
/**
Find files and directories using glob patterns.
Note that glob patterns can only contain forward-slashes, not backward-slashes, so if you want to construct a glob pattern from path components, you need to use `path.posix.join()` instead of `path.join()`.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns The stream of matching paths.
@example
```
import {globbyStream} from 'globby';
for await (const path of globbyStream('*.tmp')) {
console.log(path);
}
```
*/
export function globbyStream(
patterns: string | readonly string[],
options: Options & ({objectMode: true} | {stats: true})
): GlobbyEntryStream;
export function globbyStream(
patterns: string | readonly string[],
options?: Options
): GlobbyStream;
/**
Note that you should avoid running the same tasks multiple times as they contain a file system cache. Instead, run this method each time to ensure file system changes are taken into consideration.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns An object in the format `{pattern: string, options: object}`, which can be passed as arguments to [`fast-glob`](https://github.com/mrmlnc/fast-glob). This is useful for other globbing-related packages.
*/
export function generateGlobTasks(
patterns: string | readonly string[],
options?: Options
): Promise<GlobTask[]>;
/**
@see generateGlobTasks
@returns An object in the format `{pattern: string, options: object}`, which can be passed as arguments to [`fast-glob`](https://github.com/mrmlnc/fast-glob). This is useful for other globbing-related packages.
*/
export function generateGlobTasksSync(
patterns: string | readonly string[],
options?: Options
): GlobTask[];
/**
Note that the options affect the results.
This function is backed by [`fast-glob`](https://github.com/mrmlnc/fast-glob#isdynamicpatternpattern-options).
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3).
@returns Whether there are any special glob characters in the `patterns`.
*/
export function isDynamicPattern(
patterns: string | readonly string[],
options?: FastGlobOptionsWithoutCwd & {
/**
The current working directory in which to search.
@default process.cwd()
*/
readonly cwd?: URL | string;
}
): boolean;
/**
`.gitignore` files matched by the ignore config are not used for the resulting filter function.
@returns A filter function indicating whether a given path is ignored via a `.gitignore` file.
@example
```
import {isGitIgnored} from 'globby';
const isIgnored = await isGitIgnored();
console.log(isIgnored('some/file'));
```
*/
export function isGitIgnored(options?: GitignoreOptions): Promise<GlobbyFilterFunction>;
/**
@see isGitIgnored
@returns A filter function indicating whether a given path is ignored via a `.gitignore` file.
*/
export function isGitIgnoredSync(options?: GitignoreOptions): GlobbyFilterFunction;
/**
Converts a path to a pattern by escaping special glob characters like `()`, `[]`, `{}`. On Windows, also converts backslashes to forward slashes.
Use this when your literal paths contain characters with special meaning in globs.
@param source - A file system path to convert to a safe glob pattern.
@returns The path with special glob characters escaped.
@example
```
import {globby, convertPathToPattern} from 'globby';
// ❌ Fails - parentheses are glob syntax
await globby('C:/Program Files (x86)/*.txt');
//=> []
// ✅ Works
const base = convertPathToPattern('C:/Program Files (x86)');
await globby(`${base}/*.txt`);
//=> ['C:/Program Files (x86)/file.txt']
```
*/
export function convertPathToPattern(source: string): FastGlob.Pattern;
/**
Check if a path is ignored by the ignore files.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns A filter function indicating whether a given path is ignored via the ignore files.
This is a more generic form of the `isGitIgnored` function, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
@example
```
import {isIgnoredByIgnoreFiles} from 'globby';
const isIgnored = await isIgnoredByIgnoreFiles('**\/.gitignore');
console.log(isIgnored('some/file'));
```
*/
export function isIgnoredByIgnoreFiles(
patterns: string | readonly string[],
options?: Options
): Promise<GlobbyFilterFunction>;
/**
Check if a path is ignored by the ignore files.
@param patterns - See the supported [glob patterns](https://github.com/sindresorhus/globby#globbing-patterns).
@param options - See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones in this package.
@returns A filter function indicating whether a given path is ignored via the ignore files.
This is a more generic form of the `isGitIgnored` function, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
@see {@link isIgnoredByIgnoreFiles}
@example
```
import {isIgnoredByIgnoreFilesSync} from 'globby';
const isIgnored = isIgnoredByIgnoreFilesSync('**\/.gitignore');
console.log(isIgnored('some/file'));
```
*/
export function isIgnoredByIgnoreFilesSync(
patterns: string | readonly string[],
options?: Options
): GlobbyFilterFunction;

468
node_modules/globby/index.js generated vendored Normal file
View File

@@ -0,0 +1,468 @@
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,
} from './ignore.js';
import {
bindFsMethod,
promisifyFsMethod,
isNegativePattern,
normalizeAbsolutePatternToRelative,
normalizeDirectoryPatternForFastGlob,
adjustIgnorePatternsForParentDirectories,
convertPatternsForFastGlob,
} 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 =>
bindFsMethod(fsImplementation?.promises, 'stat')
?? bindFsMethod(fs.promises, 'stat')
?? promisifyFsMethod(fsImplementation, '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;
};
/**
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 ignoreFilesPatterns = getIgnoreFilesPatterns(options);
if (ignoreFilesPatterns.length === 0) {
return {
options,
filter: createFilterFunction(false, options.cwd),
};
}
// Read ignore files once and get both patterns and predicate
// Enable parent .gitignore search when using gitignore option
const includeParentIgnoreFiles = options.gitignore === true;
const {patterns, predicate, usingGitRoot} = await getIgnorePatternsAndPredicate(ignoreFilesPatterns, options, includeParentIgnoreFiles);
// Convert patterns to fast-glob format (may return empty array if predicate should handle everything)
const patternsForFastGlob = convertPatternsForFastGlob(patterns, usingGitRoot, normalizeDirectoryPatternForFastGlob);
const modifiedOptions = {
...options,
ignore: [...options.ignore, ...patternsForFastGlob],
};
return {
options: modifiedOptions,
filter: createFilterFunction(predicate, options.cwd),
};
};
/**
Apply gitignore patterns to options and return filter predicate (sync version).
@returns {{options: Object, filter: Function}}
*/
const applyIgnoreFilesAndGetFilterSync = options => {
const ignoreFilesPatterns = getIgnoreFilesPatterns(options);
if (ignoreFilesPatterns.length === 0) {
return {
options,
filter: createFilterFunction(false, options.cwd),
};
}
// Read ignore files once and get both patterns and predicate
// Enable parent .gitignore search when using gitignore option
const includeParentIgnoreFiles = options.gitignore === true;
const {patterns, predicate, usingGitRoot} = getIgnorePatternsAndPredicateSync(ignoreFilesPatterns, options, includeParentIgnoreFiles);
// Convert patterns to fast-glob format (may return empty array if predicate should handle everything)
const patternsForFastGlob = convertPatternsForFastGlob(patterns, usingGitRoot, normalizeDirectoryPatternForFastGlob);
const modifiedOptions = {
...options,
ignore: [...options.ignore, ...patternsForFastGlob],
};
return {
options: modifiedOptions,
filter: createFilterFunction(predicate, options.cwd),
};
};
const createFilterFunction = (isIgnored, cwd) => {
const seen = new Set();
const basePath = cwd || process.cwd();
const pathCache = new Map(); // Cache for resolved paths
return fastGlobResult => {
const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult);
// Check seen set first (fast path)
if (seen.has(pathKey)) {
return false;
}
// Only compute absolute path and check predicate if needed
if (isIgnored) {
let absolutePath = pathCache.get(pathKey);
if (absolutePath === undefined) {
absolutePath = nodePath.isAbsolute(pathKey) ? pathKey : nodePath.resolve(basePath, pathKey);
pathCache.set(pathKey, absolutePath);
// Only clear path cache if it gets too large
// Never clear 'seen' as it's needed for deduplication
if (pathCache.size > 10_000) {
pathCache.clear();
}
}
if (isIgnored(absolutePath)) {
return false;
}
}
seen.add(pathKey);
return true;
};
};
const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult));
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];
}
patterns = patterns.map(pattern => isNegativePattern(pattern)
? `!${normalizeAbsolutePatternToRelative(pattern.slice(1))}`
: pattern);
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) => {
// 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 unionFastGlobResults(results, filter);
});
export const globbySync = normalizeArgumentsSync((patterns, 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) => {
// 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 streams = tasks.map(task => fastGlob.stream(task.patterns, task.options));
if (streams.length === 0) {
return Readable.from([]);
}
const stream = mergeStreams(streams).filter(fastGlobResult => filter(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;

9
node_modules/globby/license generated vendored Normal file
View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

94
node_modules/globby/package.json generated vendored Normal file
View File

@@ -0,0 +1,94 @@
{
"name": "globby",
"version": "16.1.0",
"description": "User-friendly glob matching",
"license": "MIT",
"repository": "sindresorhus/globby",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"email": "sindresorhus@gmail.com",
"name": "Sindre Sorhus",
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"sideEffects": false,
"engines": {
"node": ">=20"
},
"scripts": {
"bench": "npm update @globby/main-branch glob-stream fast-glob && node bench.js",
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts",
"ignore.js",
"utilities.js"
],
"keywords": [
"all",
"array",
"directories",
"expand",
"files",
"filesystem",
"filter",
"find",
"fnmatch",
"folders",
"fs",
"glob",
"globbing",
"globs",
"gulpfriendly",
"match",
"matcher",
"minimatch",
"multi",
"multiple",
"paths",
"pattern",
"patterns",
"traverse",
"util",
"utility",
"wildcard",
"wildcards",
"promise",
"gitignore",
"git"
],
"dependencies": {
"@sindresorhus/merge-streams": "^4.0.0",
"fast-glob": "^3.3.3",
"ignore": "^7.0.5",
"is-path-inside": "^4.0.0",
"slash": "^5.1.0",
"unicorn-magic": "^0.4.0"
},
"devDependencies": {
"@globby/main-branch": "sindresorhus/globby#main",
"@types/node": "^24.5.2",
"ava": "^6.4.1",
"benchmark": "2.1.4",
"glob-stream": "^8.0.3",
"tempy": "^3.1.0",
"tsd": "^0.33.0",
"xo": "^1.2.2"
},
"xo": {
"ignores": [
"fixtures"
]
},
"ava": {
"files": [
"!tests/utilities.js"
],
"workerThreads": false
}
}

388
node_modules/globby/readme.md generated vendored Normal file
View File

@@ -0,0 +1,388 @@
# globby
> User-friendly glob matching
Based on [`fast-glob`](https://github.com/mrmlnc/fast-glob) but adds a bunch of useful features.
## Features
- Promise API
- Multiple patterns
- Negated patterns: `['foo*', '!foobar']`
- Negation-only patterns: `['!foobar']` → matches all files except `foobar`
- Expands directories: `foo``foo/**/*`
- Supports `.gitignore` and similar ignore config files
- Supports `URL` as `cwd`
## Install
```sh
npm install globby
```
## Usage
```
├── unicorn
├── cake
└── rainbow
```
```js
import {globby} from 'globby';
const paths = await globby(['*', '!cake']);
console.log(paths);
//=> ['unicorn', 'rainbow']
```
## API
Note that glob patterns can only contain forward-slashes, not backward-slashes, so if you want to construct a glob pattern from path components, you need to use `path.posix.join()` instead of `path.join()`.
**Windows:** Patterns with backslashes will silently fail. Use `path.posix.join()` or [`convertPathToPattern()`](#convertpathtopatternpath).
### globby(patterns, options?)
Returns a `Promise<string[]>` of matching paths.
#### patterns
Type: `string | string[]`
See supported `minimatch` [patterns](https://github.com/isaacs/minimatch#usage).
#### options
Type: `object`
See the [`fast-glob` options](https://github.com/mrmlnc/fast-glob#options-3) in addition to the ones below.
##### expandDirectories
Type: `boolean | string[] | object`\
Default: `true`
If set to `true`, `globby` will automatically glob directories for you. If you define an `Array` it will only glob files that matches the patterns inside the `Array`. You can also define an `object` with `files` and `extensions` like below:
```js
import {globby} from 'globby';
const paths = await globby('images', {
expandDirectories: {
files: ['cat', 'unicorn', '*.jpg'],
extensions: ['png']
}
});
console.log(paths);
//=> ['cat.png', 'unicorn.png', 'cow.jpg', 'rainbow.jpg']
```
Note that if you set this option to `false`, you won't get back matched directories unless you set `onlyFiles: false`.
##### gitignore
Type: `boolean`\
Default: `false`
Respect ignore patterns in `.gitignore` files that apply to the globbed files.
When enabled, globby searches for `.gitignore` files from the current working directory downward, and if a Git repository is detected (by finding a `.git` directory), it also respects `.gitignore` files in parent directories up to the repository root. This matches Git's actual behavior where patterns from parent `.gitignore` files apply to subdirectories.
Gitignore patterns take priority over user patterns, matching Git's behavior. To include gitignored files, set this to `false`.
**Performance:** Globby reads `.gitignore` files before globbing. When there are no negation patterns (like `!important.log`) and no parent `.gitignore` files are found, it passes ignore patterns to fast-glob to skip traversing ignored directories entirely, which significantly improves performance for large `node_modules` or build directories. When negation patterns or parent `.gitignore` files are present, all filtering is done after traversal to ensure correct Git-compatible behavior. For optimal performance, prefer specific `.gitignore` patterns without negations, or use `ignoreFiles: '.gitignore'` to target only the root ignore file.
##### ignoreFiles
Type: `string | string[]`\
Default: `undefined`
Glob patterns to look for ignore files, which are then used to ignore globbed files.
This is a more generic form of the `gitignore` option, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
**Performance tip:** Using a specific path like `'.gitignore'` is much faster than recursive patterns.
##### expandNegationOnlyPatterns
Type: `boolean`\
Default: `true`
When only negation patterns are provided (e.g., `['!*.json']`), automatically prepend a catch-all pattern (`**/*`) to match all files before applying negations.
Set to `false` to return an empty array when only negation patterns are provided. This can be useful when patterns are user-controlled, to avoid unexpectedly matching all files.
```js
import {globby} from 'globby';
// Default behavior: matches all files except .json
await globby(['!*.json']);
//=> ['file.txt', 'image.png', ...]
// Disable expansion: returns empty array
await globby(['!*.json'], {expandNegationOnlyPatterns: false});
//=> []
```
##### fs
Type: [`FileSystemAdapter`](https://github.com/mrmlnc/fast-glob#fs)\
Default: `undefined`
Custom file system implementation (useful for testing or virtual file systems).
**Note:** When using `gitignore` or `ignoreFiles`, the custom fs must also provide `readFile`/`readFileSync` methods.
### globbySync(patterns, options?)
Returns `string[]` of matching paths.
### globbyStream(patterns, options?)
Returns a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) of matching paths.
For example, loop over glob matches in a [`for await...of` loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) like this:
```js
import {globbyStream} from 'globby';
for await (const path of globbyStream('*.tmp')) {
console.log(path);
}
```
### convertPathToPattern(path)
Converts a path to a pattern by escaping special glob characters like `()`, `[]`, `{}`. On Windows, also converts backslashes to forward slashes.
Use this when your literal paths contain characters with special meaning in globs.
```js
import {globby, convertPathToPattern} from 'globby';
// ❌ Fails - parentheses are glob syntax
await globby('C:/Program Files (x86)/*.txt');
//=> []
// ✅ Works
const base = convertPathToPattern('C:/Program Files (x86)');
await globby(`${base}/*.txt`);
//=> ['C:/Program Files (x86)/file.txt']
```
[Learn more.](https://github.com/mrmlnc/fast-glob#convertpathtopatternpath)
### generateGlobTasks(patterns, options?)
Returns an `Promise<object[]>` in the format `{patterns: string[], options: Object}`, which can be passed as arguments to [`fast-glob`](https://github.com/mrmlnc/fast-glob). This is useful for other globbing-related packages.
Note that you should avoid running the same tasks multiple times as they contain a file system cache. Instead, run this method each time to ensure file system changes are taken into consideration.
### generateGlobTasksSync(patterns, options?)
Returns an `object[]` in the format `{patterns: string[], options: Object}`, which can be passed as arguments to [`fast-glob`](https://github.com/mrmlnc/fast-glob). This is useful for other globbing-related packages.
Takes the same arguments as `generateGlobTasks`.
### isDynamicPattern(patterns, options?)
Returns a `boolean` of whether there are any special glob characters in the `patterns`.
Note that the options affect the results.
This function is backed by [`fast-glob`](https://github.com/mrmlnc/fast-glob#isdynamicpatternpattern-options).
### isGitIgnored(options?)
Returns a `Promise<(path: URL | string) => boolean>` indicating whether a given path is ignored via a `.gitignore` file.
#### options
Type: `object`
##### cwd
Type: `URL | string`\
Default: `process.cwd()`
The current working directory in which to search.
##### suppressErrors
Type: `boolean`\
Default: `false`
Suppress errors when encountering directories or files without read permissions.
##### deep
Type: `number`\
Default: `Infinity`
Maximum depth to search for `.gitignore` files.
- `0` - Only search in the start directory
- `1` - Search in the start directory and one level of subdirectories
- `2` - Search in the start directory and two levels of subdirectories
##### ignore
Type: `string | string[]`\
Default: `[]`
Glob patterns to exclude from `.gitignore` file search.
##### followSymbolicLinks
Type: `boolean`\
Default: `true`
Indicates whether to traverse descendants of symbolic link directories.
##### concurrency
Type: `number`\
Default: `os.cpus().length`
Specifies the maximum number of concurrent requests from a reader to read directories.
##### throwErrorOnBrokenSymbolicLink
Type: `boolean`\
Default: `false`
Throw an error when symbolic link is broken if `true` or safely return `lstat` call if `false`.
##### fs
Type: [`FileSystemAdapter`](https://github.com/mrmlnc/fast-glob#fs)\
Default: `undefined`
Custom file system implementation (useful for testing or virtual file systems).
**Note:** The custom fs must provide `readFile`/`readFileSync` methods for reading `.gitignore` files.
```js
import {isGitIgnored} from 'globby';
const isIgnored = await isGitIgnored();
console.log(isIgnored('some/file'));
```
```js
// Suppress errors when encountering unreadable directories
const isIgnored = await isGitIgnored({suppressErrors: true});
```
```js
// Limit search depth and exclude certain directories
const isIgnored = await isGitIgnored({
deep: 2,
ignore: ['**/node_modules/**', '**/dist/**']
});
```
### isGitIgnoredSync(options?)
Returns a `(path: URL | string) => boolean` indicating whether a given path is ignored via a `.gitignore` file.
See [`isGitIgnored`](#isgitignoredoptions) for options.
### isIgnoredByIgnoreFiles(patterns, options?)
Returns a `Promise<(path: URL | string) => boolean>` indicating whether a given path is ignored via the ignore files.
This is a more generic form of the `isGitIgnored` function, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
#### patterns
Type: `string | string[]`
Glob patterns to look for ignore files.
#### options
Type: `object`
See [`isGitIgnored` options](#isgitignoredoptions) for all available options.
```js
import {isIgnoredByIgnoreFiles} from 'globby';
const isIgnored = await isIgnoredByIgnoreFiles("**/.gitignore");
console.log(isIgnored('some/file'));
```
```js
// Suppress errors when encountering unreadable directories
const isIgnored = await isIgnoredByIgnoreFiles("**/.eslintignore", {suppressErrors: true});
```
```js
// Limit search depth and concurrency
const isIgnored = await isIgnoredByIgnoreFiles("**/.prettierignore", {
deep: 3,
concurrency: 4
});
```
### isIgnoredByIgnoreFilesSync(patterns, options?)
Returns a `(path: URL | string) => boolean` indicating whether a given path is ignored via the ignore files.
This is a more generic form of the `isGitIgnoredSync` function, allowing you to find ignore files with a [compatible syntax](http://git-scm.com/docs/gitignore). For instance, this works with Babel's `.babelignore`, Prettier's `.prettierignore`, or ESLint's `.eslintignore` files.
See [`isIgnoredByIgnoreFiles`](#isignoredbyignorefilespatterns-options) for patterns and options.
```js
import {isIgnoredByIgnoreFilesSync} from 'globby';
const isIgnored = isIgnoredByIgnoreFilesSync("**/.gitignore");
console.log(isIgnored('some/file'));
```
## Globbing patterns
Just a quick overview.
- `*` matches any number of characters, but not `/`
- `?` matches a single character, but not `/`
- `**` matches any number of characters, including `/`, as long as it's the only thing in a path part
- `{}` allows for a comma-separated list of "or" expressions
- `!` at the beginning of a pattern will negate the match
### Negation patterns
Globby supports negation patterns to exclude files. There are two ways to use them:
**With positive patterns:**
```js
await globby(['src/**/*.js', '!src/**/*.test.js']);
// Matches all .js files except test files
```
**Negation-only patterns:**
```js
await globby(['!*.json', '!*.xml'], {cwd: 'config'});
// Matches all files in config/ except .json and .xml files
```
When using only negation patterns, globby implicitly prepends `**/*` to match all files, then applies the negations. This means `['!*.json', '!*.xml']` is equivalent to `['**/*', '!*.json', '!*.xml']`.
**Note:** The prepended `**/*` pattern respects the `dot` option. By default, dotfiles (files starting with `.`) are not matched unless you set `dot: true`.
[Various patterns and expected matches.](https://github.com/sindresorhus/multimatch/blob/main/test/test.js)
## Related
- [multimatch](https://github.com/sindresorhus/multimatch) - Match against a list instead of the filesystem
- [matcher](https://github.com/sindresorhus/matcher) - Simple wildcard matching
- [del](https://github.com/sindresorhus/del) - Delete files and directories
- [make-dir](https://github.com/sindresorhus/make-dir) - Make a directory and its parents if needed

299
node_modules/globby/utilities.js generated vendored Normal file
View File

@@ -0,0 +1,299 @@
import fs from 'node:fs';
import path from 'node:path';
import {promisify} from 'node:util';
import isPathInside from 'is-path-inside';
export const isNegativePattern = pattern => pattern[0] === '!';
/**
Normalize an absolute pattern to be relative.
On Unix, patterns starting with `/` are interpreted as absolute paths from the filesystem root. This causes inconsistent behavior across platforms since Windows uses different path roots (like `C:\`).
This function strips leading `/` to make patterns relative to cwd, ensuring consistent cross-platform behavior.
@param {string} pattern - The pattern to normalize.
*/
export const normalizeAbsolutePatternToRelative = pattern => pattern.startsWith('/') ? pattern.slice(1) : pattern;
export const bindFsMethod = (object, methodName) => {
const method = object?.[methodName];
return typeof method === 'function' ? method.bind(object) : undefined;
};
// Only used as a fallback for legacy fs implementations
export const promisifyFsMethod = (object, methodName) => {
const method = object?.[methodName];
if (typeof method !== 'function') {
return undefined;
}
return promisify(method.bind(object));
};
export const normalizeDirectoryPatternForFastGlob = pattern => {
if (!pattern.endsWith('/')) {
return pattern;
}
const trimmedPattern = pattern.replace(/\/+$/u, '');
if (!trimmedPattern) {
return '/**';
}
// Special case for '**/' to avoid producing '**/**/**'
if (trimmedPattern === '**') {
return '**/**';
}
const hasLeadingSlash = trimmedPattern.startsWith('/');
const patternBody = hasLeadingSlash ? trimmedPattern.slice(1) : trimmedPattern;
const hasInnerSlash = patternBody.includes('/');
const needsRecursivePrefix = !hasLeadingSlash && !hasInnerSlash && !trimmedPattern.startsWith('**/');
const recursivePrefix = needsRecursivePrefix ? '**/' : '';
return `${recursivePrefix}${trimmedPattern}/**`;
};
/**
Extract the parent directory prefix from a pattern (e.g., '../' or '../../').
Note: Patterns should have trailing slash after '..' (e.g., '../foo' not '..foo'). The directoryToGlob function ensures this in the normal pipeline.
@param {string} pattern - The pattern to analyze.
@returns {string} The parent directory prefix, or empty string if none.
*/
export const getParentDirectoryPrefix = pattern => {
const normalizedPattern = isNegativePattern(pattern) ? pattern.slice(1) : pattern;
const match = normalizedPattern.match(/^(\.\.\/)+/);
return match ? match[0] : '';
};
/**
Adjust ignore patterns to match the relative base of the main patterns.
When patterns reference parent directories, ignore patterns starting with globstars need to be adjusted to match from the same base directory. This ensures intuitive behavior where ignore patterns work correctly with parent directory patterns.
This is analogous to how node-glob normalizes path prefixes (see node-glob issue #309) and how Rust ignore crate strips path prefixes before matching.
@param {string[]} patterns - The main glob patterns.
@param {string[]} ignorePatterns - The ignore patterns to adjust.
@returns {string[]} Adjusted ignore patterns.
*/
export const adjustIgnorePatternsForParentDirectories = (patterns, ignorePatterns) => {
// Early exit for empty arrays
if (patterns.length === 0 || ignorePatterns.length === 0) {
return ignorePatterns;
}
// Get parent directory prefixes for all patterns (empty string if no prefix)
const parentPrefixes = patterns.map(pattern => getParentDirectoryPrefix(pattern));
// Check if all patterns have the same parent prefix
const firstPrefix = parentPrefixes[0];
if (!firstPrefix) {
return ignorePatterns; // No parent directories in any pattern
}
const allSamePrefix = parentPrefixes.every(prefix => prefix === firstPrefix);
if (!allSamePrefix) {
return ignorePatterns; // Mixed bases - don't adjust
}
// Adjust ignore patterns that start with **/
return ignorePatterns.map(pattern => {
// Only adjust patterns starting with **/ that don't already have a parent reference
if (pattern.startsWith('**/') && !pattern.startsWith('../')) {
return firstPrefix + pattern;
}
return pattern;
});
};
/**
Find the git root directory by searching upward for a .git directory.
@param {string} cwd - The directory to start searching from.
@param {Object} [fsImplementation] - Optional fs implementation.
@returns {string|undefined} The git root directory path, or undefined if not found.
*/
const getAsyncStatMethod = fsImplementation =>
bindFsMethod(fsImplementation?.promises, 'stat')
?? bindFsMethod(fs.promises, 'stat');
const getStatSyncMethod = fsImplementation => {
if (fsImplementation) {
return bindFsMethod(fsImplementation, 'statSync');
}
return bindFsMethod(fs, 'statSync');
};
const pathHasGitDirectory = stats => Boolean(stats?.isDirectory?.() || stats?.isFile?.());
const buildPathChain = (startPath, rootPath) => {
const chain = [];
let currentPath = startPath;
chain.push(currentPath);
while (currentPath !== rootPath) {
const parentPath = path.dirname(currentPath);
if (parentPath === currentPath) {
break;
}
currentPath = parentPath;
chain.push(currentPath);
}
return chain;
};
const findGitRootInChain = async (paths, statMethod) => {
for (const directory of paths) {
const gitPath = path.join(directory, '.git');
try {
const stats = await statMethod(gitPath); // eslint-disable-line no-await-in-loop
if (pathHasGitDirectory(stats)) {
return directory;
}
} catch {
// Ignore errors and continue searching
}
}
return undefined;
};
const findGitRootSyncUncached = (cwd, fsImplementation) => {
const statSyncMethod = getStatSyncMethod(fsImplementation);
if (!statSyncMethod) {
return undefined;
}
const currentPath = path.resolve(cwd);
const {root} = path.parse(currentPath);
const chain = buildPathChain(currentPath, root);
for (const directory of chain) {
const gitPath = path.join(directory, '.git');
try {
const stats = statSyncMethod(gitPath);
if (pathHasGitDirectory(stats)) {
return directory;
}
} catch {
// Ignore errors and continue searching
}
}
return undefined;
};
export const findGitRootSync = (cwd, fsImplementation) => {
if (typeof cwd !== 'string') {
throw new TypeError('cwd must be a string');
}
return findGitRootSyncUncached(cwd, fsImplementation);
};
const findGitRootAsyncUncached = async (cwd, fsImplementation) => {
const statMethod = getAsyncStatMethod(fsImplementation);
if (!statMethod) {
return findGitRootSync(cwd, fsImplementation);
}
const currentPath = path.resolve(cwd);
const {root} = path.parse(currentPath);
const chain = buildPathChain(currentPath, root);
return findGitRootInChain(chain, statMethod);
};
export const findGitRoot = async (cwd, fsImplementation) => {
if (typeof cwd !== 'string') {
throw new TypeError('cwd must be a string');
}
return findGitRootAsyncUncached(cwd, fsImplementation);
};
/**
Get paths to all .gitignore files from git root to cwd (inclusive).
@param {string} gitRoot - The git root directory.
@param {string} cwd - The current working directory.
@returns {string[]} Array of .gitignore file paths to search for.
*/
const isWithinGitRoot = (gitRoot, cwd) => {
const resolvedGitRoot = path.resolve(gitRoot);
const resolvedCwd = path.resolve(cwd);
return resolvedCwd === resolvedGitRoot || isPathInside(resolvedCwd, resolvedGitRoot);
};
export const getParentGitignorePaths = (gitRoot, cwd) => {
if (gitRoot && typeof gitRoot !== 'string') {
throw new TypeError('gitRoot must be a string or undefined');
}
if (typeof cwd !== 'string') {
throw new TypeError('cwd must be a string');
}
// If no gitRoot provided, return empty array
if (!gitRoot) {
return [];
}
if (!isWithinGitRoot(gitRoot, cwd)) {
return [];
}
const chain = buildPathChain(path.resolve(cwd), path.resolve(gitRoot));
return [...chain]
.reverse()
.map(directory => path.join(directory, '.gitignore'));
};
/**
Convert ignore patterns to fast-glob compatible format.
Returns empty array if patterns should be handled by predicate only.
@param {string[]} patterns - Ignore patterns from .gitignore files
@param {boolean} usingGitRoot - Whether patterns are relative to git root
@param {Function} normalizeDirectoryPatternForFastGlob - Function to normalize directory patterns
@returns {string[]} Patterns safe to pass to fast-glob, or empty array
*/
export const convertPatternsForFastGlob = (patterns, usingGitRoot, normalizeDirectoryPatternForFastGlob) => {
// Determine which patterns are safe to pass to fast-glob
// If there are negation patterns, we can't pass file patterns to fast-glob
// because fast-glob doesn't understand negations and would filter out files
// that should be re-included by negation patterns.
// If we're using git root, patterns are relative to git root not cwd,
// so we can't pass them to fast-glob which expects cwd-relative patterns.
// We only pass patterns to fast-glob if there are NO negations AND we're not using git root.
if (usingGitRoot) {
return []; // Patterns are relative to git root, not cwd
}
const result = [];
let hasNegations = false;
// Single pass to check for negations and collect positive patterns
for (const pattern of patterns) {
if (isNegativePattern(pattern)) {
hasNegations = true;
break; // Early exit on first negation
}
result.push(normalizeDirectoryPatternForFastGlob(pattern));
}
return hasNegations ? [] : result;
};