123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- import process from 'node:process';
- import fs from 'node:fs';
- import nodePath from 'node:path';
- import mergeStreams from '@sindresorhus/merge-streams';
- import fastGlob from 'fast-glob';
- import {isDirectory, isDirectorySync} from 'path-type';
- import {toPath} from 'unicorn-magic';
- import {
- GITIGNORE_FILES_PATTERN,
- isIgnoredByIgnoreFiles,
- isIgnoredByIgnoreFilesSync,
- } from './ignore.js';
- import {isNegativePattern} 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 normalizePathForDirectoryGlob = (filePath, cwd) => {
- const path = isNegativePattern(filePath) ? filePath.slice(1) : filePath;
- return nodePath.isAbsolute(path) ? path : nodePath.join(cwd, path);
- };
- 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,
- } = {}) => {
- const globs = await Promise.all(directoryPaths.map(async directoryPath =>
- (await isDirectory(normalizePathForDirectoryGlob(directoryPath, cwd))) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath),
- );
- return globs.flat();
- };
- const directoryToGlobSync = (directoryPaths, {
- cwd = process.cwd(),
- files,
- extensions,
- } = {}) => directoryPaths.flatMap(directoryPath => isDirectorySync(normalizePathForDirectoryGlob(directoryPath, cwd)) ? getDirectoryGlob({directoryPath, files, extensions}) : directoryPath);
- const toPatternsArray = patterns => {
- patterns = [...new Set([patterns].flat())];
- assertPatternsInput(patterns);
- return patterns;
- };
- const checkCwdOption = cwd => {
- if (!cwd) {
- return;
- }
- let stat;
- try {
- stat = fs.statSync(cwd);
- } catch {
- return;
- }
- if (!stat.isDirectory()) {
- throw new Error('The `cwd` option must be a path to a directory');
- }
- };
- const normalizeOptions = (options = {}) => {
- options = {
- ...options,
- ignore: options.ignore ?? [],
- expandDirectories: options.expandDirectories ?? true,
- cwd: toPath(options.cwd),
- };
- checkCwdOption(options.cwd);
- 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 getFilter = async options => {
- const ignoreFilesPatterns = getIgnoreFilesPatterns(options);
- return createFilterFunction(
- ignoreFilesPatterns.length > 0 && await isIgnoredByIgnoreFiles(ignoreFilesPatterns, options),
- );
- };
- const getFilterSync = options => {
- const ignoreFilesPatterns = getIgnoreFilesPatterns(options);
- return createFilterFunction(
- ignoreFilesPatterns.length > 0 && isIgnoredByIgnoreFilesSync(ignoreFilesPatterns, options),
- );
- };
- const createFilterFunction = isIgnored => {
- const seen = new Set();
- return fastGlobResult => {
- const pathKey = nodePath.normalize(fastGlobResult.path ?? fastGlobResult);
- if (seen.has(pathKey) || (isIgnored && isIgnored(pathKey))) {
- return false;
- }
- seen.add(pathKey);
- return true;
- };
- };
- const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult));
- const convertNegativePatterns = (patterns, options) => {
- 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 normalizeExpandDirectoriesOption = (options, cwd) => ({
- ...(cwd ? {cwd} : {}),
- ...(Array.isArray(options) ? {files: options} : options),
- });
- const generateTasks = async (patterns, options) => {
- const globTasks = convertNegativePatterns(patterns, options);
- const {cwd, expandDirectories} = options;
- if (!expandDirectories) {
- return globTasks;
- }
- const directoryToGlobOptions = normalizeExpandDirectoriesOption(expandDirectories, cwd);
- return Promise.all(
- globTasks.map(async task => {
- let {patterns, options} = task;
- [
- patterns,
- options.ignore,
- ] = await Promise.all([
- directoryToGlob(patterns, directoryToGlobOptions),
- directoryToGlob(options.ignore, {cwd}),
- ]);
- return {patterns, options};
- }),
- );
- };
- const generateTasksSync = (patterns, options) => {
- const globTasks = convertNegativePatterns(patterns, options);
- const {cwd, expandDirectories} = options;
- if (!expandDirectories) {
- return globTasks;
- }
- const directoryToGlobSyncOptions = normalizeExpandDirectoriesOption(expandDirectories, cwd);
- return globTasks.map(task => {
- let {patterns, options} = task;
- patterns = directoryToGlobSync(patterns, directoryToGlobSyncOptions);
- options.ignore = directoryToGlobSync(options.ignore, {cwd});
- return {patterns, options};
- });
- };
- export const globby = normalizeArguments(async (patterns, options) => {
- const [
- tasks,
- filter,
- ] = await Promise.all([
- generateTasks(patterns, options),
- getFilter(options),
- ]);
- const results = await Promise.all(tasks.map(task => fastGlob(task.patterns, task.options)));
- return unionFastGlobResults(results, filter);
- });
- export const globbySync = normalizeArgumentsSync((patterns, options) => {
- const tasks = generateTasksSync(patterns, options);
- const filter = getFilterSync(options);
- const results = tasks.map(task => fastGlob.sync(task.patterns, task.options));
- return unionFastGlobResults(results, filter);
- });
- export const globbyStream = normalizeArgumentsSync((patterns, options) => {
- const tasks = generateTasksSync(patterns, options);
- const filter = getFilterSync(options);
- const streams = tasks.map(task => fastGlob.stream(task.patterns, task.options));
- const stream = mergeStreams(streams).filter(fastGlobResult => filter(fastGlobResult));
- // TODO: Make it return a web stream at some point.
- // 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;
|