Comparing version 12.2.0 to 13.0.0
@@ -7,10 +7,13 @@ import process from 'node:process'; | ||
import slash from 'slash'; | ||
import toPath from './to-path.js'; | ||
import {toPath} from './utilities.js'; | ||
const DEFAULT_IGNORE = [ | ||
'**/node_modules/**', | ||
'**/flow-typed/**', | ||
'**/coverage/**', | ||
'**/.git', | ||
]; | ||
const gitignoreGlobOptions = { | ||
ignore: [ | ||
'**/node_modules', | ||
'**/flow-typed', | ||
'**/coverage', | ||
'**/.git', | ||
], | ||
absolute: true, | ||
}; | ||
@@ -60,51 +63,40 @@ const mapGitIgnorePatternTo = base => ignore => { | ||
const getIsIgnoredPredicate = (ignores, cwd) => p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, toPath(p.path || p))))); | ||
const getIsIgnoredPredicate = (ignores, cwd) => p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, toPath(p))))); | ||
const getFile = async (file, cwd) => { | ||
const filePath = path.join(cwd, file); | ||
const content = await fs.promises.readFile(filePath, 'utf8'); | ||
const getFile = async (filePath, cwd) => ({ | ||
cwd, | ||
filePath, | ||
content: await fs.promises.readFile(filePath, 'utf8'), | ||
}); | ||
return { | ||
cwd, | ||
filePath, | ||
content, | ||
}; | ||
}; | ||
const getFileSync = (filePath, cwd) => ({ | ||
cwd, | ||
filePath, | ||
content: fs.readFileSync(filePath, 'utf8'), | ||
}); | ||
const getFileSync = (file, cwd) => { | ||
const filePath = path.join(cwd, file); | ||
const content = fs.readFileSync(filePath, 'utf8'); | ||
const normalizeOptions = (options = {}) => ({ | ||
cwd: toPath(options.cwd) || slash(process.cwd()), | ||
}); | ||
return { | ||
cwd, | ||
filePath, | ||
content, | ||
}; | ||
}; | ||
const normalizeOptions = ({ | ||
ignore = [], | ||
cwd = slash(process.cwd()), | ||
} = {}) => ({ignore: [...DEFAULT_IGNORE, ...ignore], cwd: toPath(cwd)}); | ||
export const isGitIgnored = async options => { | ||
options = normalizeOptions(options); | ||
const {cwd} = normalizeOptions(options); | ||
const paths = await fastGlob('**/.gitignore', options); | ||
const paths = await fastGlob('**/.gitignore', {cwd, ...gitignoreGlobOptions}); | ||
const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); | ||
const files = await Promise.all(paths.map(file => getFile(file, cwd))); | ||
const ignores = reduceIgnore(files); | ||
return getIsIgnoredPredicate(ignores, options.cwd); | ||
return getIsIgnoredPredicate(ignores, cwd); | ||
}; | ||
export const isGitIgnoredSync = options => { | ||
options = normalizeOptions(options); | ||
const {cwd} = normalizeOptions(options); | ||
const paths = fastGlob.sync('**/.gitignore', options); | ||
const paths = fastGlob.sync('**/.gitignore', {cwd, ...gitignoreGlobOptions}); | ||
const files = paths.map(file => getFileSync(file, options.cwd)); | ||
const files = paths.map(file => getFileSync(file, cwd)); | ||
const ignores = reduceIgnore(files); | ||
return getIsIgnoredPredicate(ignores, options.cwd); | ||
return getIsIgnoredPredicate(ignores, cwd); | ||
}; |
@@ -7,3 +7,3 @@ import {URL} from 'node:url'; // TODO: Remove this when https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34960 is fixed. | ||
export interface GlobTask { | ||
readonly pattern: string; | ||
readonly patterns: string[]; | ||
readonly options: Options; | ||
@@ -61,3 +61,2 @@ } | ||
readonly cwd?: URL | string; | ||
readonly ignore?: readonly string[]; | ||
} | ||
@@ -146,2 +145,12 @@ | ||
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[]; | ||
@@ -148,0 +157,0 @@ |
249
index.js
import fs from 'node:fs'; | ||
import arrayUnion from 'array-union'; | ||
import merge2 from 'merge2'; | ||
import fastGlob from 'fast-glob'; | ||
import dirGlob from 'dir-glob'; | ||
import toPath from './to-path.js'; | ||
import {isGitIgnored, isGitIgnoredSync} from './gitignore.js'; | ||
import {FilterStream, UniqueStream} from './stream-utils.js'; | ||
import {FilterStream, toPath} from './utilities.js'; | ||
const DEFAULT_FILTER = () => false; | ||
const isNegative = pattern => pattern[0] === '!'; | ||
@@ -20,2 +16,8 @@ | ||
const toPatternsArray = patterns => { | ||
patterns = [...new Set([patterns].flat())]; | ||
assertPatternsInput(patterns); | ||
return patterns; | ||
}; | ||
const checkCwdOption = options => { | ||
@@ -38,159 +40,166 @@ if (!options.cwd) { | ||
const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; | ||
export const generateGlobTasks = (patterns, taskOptions = {}) => { | ||
patterns = arrayUnion([patterns].flat()); | ||
assertPatternsInput(patterns); | ||
const globTasks = []; | ||
taskOptions = { | ||
const normalizeOptions = (options = {}) => { | ||
options = { | ||
ignore: [], | ||
expandDirectories: true, | ||
...taskOptions, | ||
cwd: toPath(taskOptions.cwd), | ||
...options, | ||
cwd: toPath(options.cwd), | ||
}; | ||
checkCwdOption(taskOptions); | ||
checkCwdOption(options); | ||
for (const [index, pattern] of patterns.entries()) { | ||
if (isNegative(pattern)) { | ||
continue; | ||
} | ||
return options; | ||
}; | ||
const ignore = patterns | ||
.slice(index) | ||
.filter(pattern => isNegative(pattern)) | ||
.map(pattern => pattern.slice(1)); | ||
const normalizeArguments = fn => async (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options)); | ||
const normalizeArgumentsSync = fn => (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options)); | ||
const options = { | ||
...taskOptions, | ||
ignore: [...taskOptions.ignore, ...ignore], | ||
}; | ||
const getFilter = async options => createFilterFunction( | ||
options.gitignore && await isGitIgnored({cwd: options.cwd}), | ||
); | ||
globTasks.push({pattern, options}); | ||
} | ||
const getFilterSync = options => createFilterFunction( | ||
options.gitignore && isGitIgnoredSync({cwd: options.cwd}), | ||
); | ||
return globTasks; | ||
const createFilterFunction = isIgnored => { | ||
const seen = new Set(); | ||
return fastGlobResult => { | ||
const path = fastGlobResult.path || fastGlobResult; | ||
const seenOrIgnored = seen.has(path) || (isIgnored && isIgnored(path)); | ||
seen.add(path); | ||
return !seenOrIgnored; | ||
}; | ||
}; | ||
const globDirectories = (task, fn) => { | ||
let options = {}; | ||
if (task.options.cwd) { | ||
options.cwd = task.options.cwd; | ||
} | ||
const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult)); | ||
const unionFastGlobStreams = (streams, filter) => merge2(streams).pipe(new FilterStream(fastGlobResult => filter(fastGlobResult))); | ||
if (Array.isArray(task.options.expandDirectories)) { | ||
options = { | ||
...options, | ||
files: task.options.expandDirectories, | ||
}; | ||
} else if (typeof task.options.expandDirectories === 'object') { | ||
options = { | ||
...options, | ||
...task.options.expandDirectories, | ||
}; | ||
} | ||
const convertNegativePatterns = (patterns, options) => { | ||
const tasks = []; | ||
return fn(task.pattern, options); | ||
}; | ||
while (patterns.length > 0) { | ||
const index = patterns.findIndex(pattern => isNegative(pattern)); | ||
const getPattern = (task, fn) => task.options.expandDirectories ? globDirectories(task, fn) : [task.pattern]; | ||
if (index === -1) { | ||
tasks.push({patterns, options}); | ||
break; | ||
} | ||
const getFilterSync = options => options && options.gitignore | ||
? isGitIgnoredSync({cwd: options.cwd, ignore: options.ignore}) | ||
: DEFAULT_FILTER; | ||
const ignorePattern = patterns[index].slice(1); | ||
const globToTask = task => async glob => { | ||
const {options} = task; | ||
if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { | ||
options.ignore = await dirGlob(options.ignore); | ||
} | ||
for (const task of tasks) { | ||
task.options.ignore.push(ignorePattern); | ||
} | ||
return { | ||
pattern: glob, | ||
options, | ||
}; | ||
}; | ||
if (index !== 0) { | ||
tasks.push({ | ||
patterns: patterns.slice(0, index), | ||
options: { | ||
...options, | ||
ignore: [ | ||
...options.ignore, | ||
ignorePattern, | ||
], | ||
}, | ||
}); | ||
} | ||
const globToTaskSync = task => glob => { | ||
const {options} = task; | ||
if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { | ||
options.ignore = dirGlob.sync(options.ignore); | ||
patterns = patterns.slice(index + 1); | ||
} | ||
return { | ||
pattern: glob, | ||
options, | ||
}; | ||
return tasks; | ||
}; | ||
export const globby = async (patterns, options) => { | ||
const globTasks = generateGlobTasks(patterns, options); | ||
const getDirGlobOptions = (options, cwd) => ({ | ||
...(cwd ? {cwd} : {}), | ||
...(Array.isArray(options) ? {files: options} : options), | ||
}); | ||
const getFilter = async () => options && options.gitignore | ||
? isGitIgnored({cwd: options.cwd, ignore: options.ignore}) | ||
: DEFAULT_FILTER; | ||
const generateTasks = async (patterns, options) => { | ||
const globTasks = convertNegativePatterns(patterns, options); | ||
const getTasks = async () => { | ||
const tasks = await Promise.all(globTasks.map(async task => { | ||
const globs = await getPattern(task, dirGlob); | ||
return Promise.all(globs.map(globToTask(task))); | ||
})); | ||
const {cwd, expandDirectories} = options; | ||
return arrayUnion(...tasks); | ||
}; | ||
if (!expandDirectories) { | ||
return globTasks; | ||
} | ||
const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); | ||
const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); | ||
const patternExpandOptions = getDirGlobOptions(expandDirectories, cwd); | ||
const ignoreExpandOptions = cwd ? {cwd} : undefined; | ||
return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); | ||
return Promise.all( | ||
globTasks.map(async task => { | ||
let {patterns, options} = task; | ||
[ | ||
patterns, | ||
options.ignore, | ||
] = await Promise.all([ | ||
dirGlob(patterns, patternExpandOptions), | ||
dirGlob(options.ignore, ignoreExpandOptions), | ||
]); | ||
return {patterns, options}; | ||
}), | ||
); | ||
}; | ||
export const globbySync = (patterns, options) => { | ||
const globTasks = generateGlobTasks(patterns, options); | ||
const generateTasksSync = (patterns, options) => { | ||
const globTasks = convertNegativePatterns(patterns, options); | ||
const tasks = []; | ||
for (const task of globTasks) { | ||
const newTask = getPattern(task, dirGlob.sync).map(globToTaskSync(task)); | ||
tasks.push(...newTask); | ||
const {cwd, expandDirectories} = options; | ||
if (!expandDirectories) { | ||
return globTasks; | ||
} | ||
const filter = getFilterSync(options); | ||
const patternExpandOptions = getDirGlobOptions(expandDirectories, cwd); | ||
const ignoreExpandOptions = cwd ? {cwd} : undefined; | ||
let matches = []; | ||
for (const task of tasks) { | ||
matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); | ||
} | ||
return matches.filter(path_ => !filter(path_)); | ||
return globTasks.map(task => { | ||
let {patterns, options} = task; | ||
patterns = dirGlob.sync(patterns, patternExpandOptions); | ||
options.ignore = dirGlob.sync(options.ignore, ignoreExpandOptions); | ||
return {patterns, options}; | ||
}); | ||
}; | ||
export const globbyStream = (patterns, options) => { | ||
const globTasks = generateGlobTasks(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))); | ||
const tasks = []; | ||
for (const task of globTasks) { | ||
const newTask = getPattern(task, dirGlob.sync).map(globToTaskSync(task)); | ||
tasks.push(...newTask); | ||
} | ||
return unionFastGlobResults(results, filter); | ||
}); | ||
export const globbySync = normalizeArgumentsSync((patterns, options) => { | ||
const tasks = generateTasksSync(patterns, options); | ||
const filter = getFilterSync(options); | ||
const filterStream = new FilterStream(p => !filter(p)); | ||
const uniqueStream = new UniqueStream(); | ||
const results = tasks.map(task => fastGlob.sync(task.patterns, task.options)); | ||
return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) | ||
.pipe(filterStream) | ||
.pipe(uniqueStream); | ||
}; | ||
return unionFastGlobResults(results, filter); | ||
}); | ||
export const isDynamicPattern = (patterns, options = {}) => { | ||
options = { | ||
...options, | ||
cwd: toPath(options.cwd), | ||
}; | ||
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)); | ||
return [patterns].flat().some(pattern => fastGlob.isDynamicPattern(pattern, options)); | ||
}; | ||
return unionFastGlobStreams(streams, filter); | ||
}); | ||
export const isDynamicPattern = normalizeArgumentsSync( | ||
(patterns, options) => patterns.some(pattern => fastGlob.isDynamicPattern(pattern, options)), | ||
); | ||
export const generateGlobTasks = normalizeArguments(generateTasks); | ||
export const generateGlobTasksSync = normalizeArgumentsSync(generateTasksSync); | ||
export { | ||
@@ -197,0 +206,0 @@ isGitIgnored, |
{ | ||
"name": "globby", | ||
"version": "12.2.0", | ||
"version": "13.0.0", | ||
"description": "User-friendly glob matching", | ||
@@ -19,3 +19,3 @@ "license": "MIT", | ||
"scripts": { | ||
"bench": "npm update glob-stream fast-glob && matcha bench.js", | ||
"bench": "npm update @globby/main-branch glob-stream fast-glob && node bench.js", | ||
"test": "xo && ava && tsd" | ||
@@ -27,4 +27,3 @@ }, | ||
"gitignore.js", | ||
"stream-utils.js", | ||
"to-path.js" | ||
"utilities.js" | ||
], | ||
@@ -65,6 +64,5 @@ "keywords": [ | ||
"dependencies": { | ||
"array-union": "^3.0.1", | ||
"dir-glob": "^3.0.1", | ||
"fast-glob": "^3.2.7", | ||
"ignore": "^5.1.9", | ||
"fast-glob": "^3.2.11", | ||
"ignore": "^5.2.0", | ||
"merge2": "^1.4.1", | ||
@@ -74,11 +72,11 @@ "slash": "^4.0.0" | ||
"devDependencies": { | ||
"@types/node": "^16.11.11", | ||
"ava": "^3.15.0", | ||
"@globby/main-branch": "sindresorhus/globby#main", | ||
"@types/node": "^17.0.10", | ||
"ava": "^4.0.1", | ||
"benchmark": "2.1.4", | ||
"get-stream": "^6.0.1", | ||
"glob-stream": "^7.0.0", | ||
"globby": "sindresorhus/globby#main", | ||
"matcha": "^0.7.0", | ||
"rimraf": "^3.0.2", | ||
"tsd": "^0.19.0", | ||
"typescript": "^4.5.2", | ||
"tsd": "^0.19.1", | ||
"typescript": "^4.5.5", | ||
"xo": "^0.47.0" | ||
@@ -90,3 +88,9 @@ }, | ||
] | ||
}, | ||
"ava": { | ||
"files": [ | ||
"!tests/utilities.js" | ||
], | ||
"workerThreads": false | ||
} | ||
} |
@@ -113,6 +113,12 @@ # globby | ||
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. | ||
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?) | ||
@@ -130,3 +136,3 @@ | ||
Takes `cwd?: URL | string` and `ignore?: string[]` as options. `.gitignore` files matched by the ignore config are not used for the resulting filter function. | ||
Takes `cwd?: URL | string` as options. | ||
@@ -145,3 +151,3 @@ ```js | ||
Takes the same options as `isGitIgnored`. | ||
Takes `cwd?: URL | string` as options. | ||
@@ -148,0 +154,0 @@ ## Globbing patterns |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23075
5
175
7
404
- Removedarray-union@^3.0.1
- Removedarray-union@3.0.1(transitive)
Updatedfast-glob@^3.2.11
Updatedignore@^5.2.0