Skip to content

Commit 51c8f68

Browse files
authored
Expose new generateGlobTasks and generateGlobTasksSync (#221)
1 parent 93f83f3 commit 51c8f68

9 files changed

Lines changed: 230 additions & 95 deletions

File tree

index.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {Options as FastGlobOptions, Entry} from 'fast-glob';
44
export type GlobEntry = Entry;
55

66
export interface GlobTask {
7-
readonly pattern: string;
7+
readonly patterns: string[];
88
readonly options: Options;
99
}
1010

@@ -141,6 +141,16 @@ Note that you should avoid running the same tasks multiple times as they contain
141141
export function generateGlobTasks(
142142
patterns: string | readonly string[],
143143
options?: Options
144+
): Promise<GlobTask[]>;
145+
146+
/**
147+
@see generateGlobTasks
148+
149+
@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.
150+
*/
151+
export function generateGlobTasksSync(
152+
patterns: string | readonly string[],
153+
options?: Options
144154
): GlobTask[];
145155

146156
/**

index.js

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ const normalizeOptions = (options = {}) => {
4949
return options;
5050
};
5151

52-
const normalizeArguments = fn => (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));
52+
const normalizeArguments = fn => async (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));
53+
const normalizeArgumentsSync = fn => (patterns, options) => fn(toPatternsArray(patterns), normalizeOptions(options));
5354

5455
const getFilter = async options => createFilterFunction(
5556
options.gitignore && await isGitIgnored({cwd: options.cwd, ignore: options.ignore}),
@@ -71,7 +72,7 @@ const createFilterFunction = isIgnored => {
7172
const unionFastGlobResults = (results, filter) => results.flat().filter(fastGlobResult => filter(fastGlobResult));
7273
const unionFastGlobStreams = (streams, filter) => merge2(streams).pipe(new FilterStream(fastGlobResult => filter(fastGlobResult)));
7374

74-
const generateGlobTasksInternal = (patterns, taskOptions) => {
75+
const convertNegativePatterns = (patterns, taskOptions) => {
7576
const globTasks = [];
7677
for (const [index, pattern] of patterns.entries()) {
7778
if (isNegative(pattern)) {
@@ -88,7 +89,7 @@ const generateGlobTasksInternal = (patterns, taskOptions) => {
8889
ignore: [...taskOptions.ignore, ...ignore],
8990
};
9091

91-
globTasks.push({pattern, options});
92+
globTasks.push({patterns: [pattern], options});
9293
}
9394

9495
return globTasks;
@@ -100,7 +101,7 @@ const getDirGlobOptions = (options, cwd) => ({
100101
});
101102

102103
const generateTasks = async (patterns, options) => {
103-
const globTasks = generateGlobTasksInternal(patterns, options);
104+
const globTasks = convertNegativePatterns(patterns, options);
104105

105106
const {cwd, expandDirectories} = options;
106107

@@ -113,24 +114,23 @@ const generateTasks = async (patterns, options) => {
113114

114115
return Promise.all(
115116
globTasks.map(async task => {
116-
const {pattern, options} = task;
117+
let {patterns, options} = task;
117118

118-
const [
119+
[
119120
patterns,
120-
ignore,
121+
options.ignore,
121122
] = await Promise.all([
122-
dirGlob(pattern, patternExpandOptions),
123+
dirGlob(patterns, patternExpandOptions),
123124
dirGlob(options.ignore, ignoreExpandOptions),
124125
]);
125126

126-
options.ignore = ignore;
127-
return {pattern: patterns, options};
127+
return {patterns, options};
128128
}),
129129
);
130130
};
131131

132132
const generateTasksSync = (patterns, options) => {
133-
const globTasks = generateGlobTasksInternal(patterns, options);
133+
const globTasks = convertNegativePatterns(patterns, options);
134134

135135
const {cwd, expandDirectories} = options;
136136

@@ -142,50 +142,48 @@ const generateTasksSync = (patterns, options) => {
142142
const ignoreExpandOptions = cwd ? {cwd} : undefined;
143143

144144
return globTasks.map(task => {
145-
const {pattern, options} = task;
146-
const patterns = dirGlob.sync(pattern, patternExpandOptions);
145+
let {patterns, options} = task;
146+
patterns = dirGlob.sync(patterns, patternExpandOptions);
147147
options.ignore = dirGlob.sync(options.ignore, ignoreExpandOptions);
148-
return {pattern: patterns, options};
148+
return {patterns, options};
149149
});
150150
};
151151

152-
export const globby = async (patterns, options) => {
153-
patterns = toPatternsArray(patterns);
154-
options = normalizeOptions(options);
155-
152+
export const globby = normalizeArguments(async (patterns, options) => {
156153
const [
157154
tasks,
158155
filter,
159156
] = await Promise.all([
160157
generateTasks(patterns, options),
161158
getFilter(options),
162159
]);
163-
const results = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options)));
160+
const results = await Promise.all(tasks.map(task => fastGlob(task.patterns, task.options)));
164161

165162
return unionFastGlobResults(results, filter);
166-
};
163+
});
167164

168-
export const globbySync = normalizeArguments((patterns, options) => {
165+
export const globbySync = normalizeArgumentsSync((patterns, options) => {
169166
const tasks = generateTasksSync(patterns, options);
170167
const filter = getFilterSync(options);
171-
const results = tasks.map(task => fastGlob.sync(task.pattern, task.options));
168+
const results = tasks.map(task => fastGlob.sync(task.patterns, task.options));
172169

173170
return unionFastGlobResults(results, filter);
174171
});
175172

176-
export const globbyStream = normalizeArguments((patterns, options) => {
173+
export const globbyStream = normalizeArgumentsSync((patterns, options) => {
177174
const tasks = generateTasksSync(patterns, options);
178175
const filter = getFilterSync(options);
179-
const streams = tasks.map(task => fastGlob.stream(task.pattern, task.options));
176+
const streams = tasks.map(task => fastGlob.stream(task.patterns, task.options));
180177

181178
return unionFastGlobStreams(streams, filter);
182179
});
183180

184-
export const isDynamicPattern = normalizeArguments(
181+
export const isDynamicPattern = normalizeArgumentsSync(
185182
(patterns, options) => patterns.some(pattern => fastGlob.isDynamicPattern(pattern, options)),
186183
);
187184

188-
export const generateGlobTasks = normalizeArguments(generateGlobTasksInternal);
185+
export const generateGlobTasks = normalizeArguments(generateTasks);
186+
export const generateGlobTasksSync = normalizeArgumentsSync(generateTasksSync);
189187

190188
export {
191189
isGitIgnored,

index.test-d.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
globbySync,
1010
globbyStream,
1111
generateGlobTasks,
12+
generateGlobTasksSync,
1213
isDynamicPattern,
1314
isGitIgnored,
1415
isGitIgnoredSync,
@@ -83,23 +84,42 @@ expectType<NodeJS.ReadableStream>(globbyStream('*.tmp', {ignore: ['**/b.tmp']}))
8384
})();
8485

8586
// GenerateGlobTasks
86-
expectType<GlobTask[]>(generateGlobTasks('*.tmp'));
87-
expectType<GlobTask[]>(generateGlobTasks(['a.tmp', '*.tmp', '!{c,d,e}.tmp']));
87+
expectType<Promise<GlobTask[]>>(generateGlobTasks('*.tmp'));
88+
expectType<Promise<GlobTask[]>>(generateGlobTasks(['a.tmp', '*.tmp', '!{c,d,e}.tmp']));
8889

89-
expectType<GlobTask[]>(generateGlobTasks('*.tmp', {expandDirectories: false}));
90-
expectType<GlobTask[]>(
90+
expectType<Promise<GlobTask[]>>(generateGlobTasks('*.tmp', {expandDirectories: false}));
91+
expectType<Promise<GlobTask[]>>(
9192
generateGlobTasks('*.tmp', {expandDirectories: ['a*', 'b*']}),
9293
);
93-
expectType<GlobTask[]>(
94+
expectType<Promise<GlobTask[]>>(
9495
generateGlobTasks('*.tmp', {
9596
expandDirectories: {
9697
files: ['a', 'b'],
9798
extensions: ['tmp'],
9899
},
99100
}),
100101
);
101-
expectType<GlobTask[]>(generateGlobTasks('*.tmp', {gitignore: true}));
102-
expectType<GlobTask[]>(generateGlobTasks('*.tmp', {ignore: ['**/b.tmp']}));
102+
expectType<Promise<GlobTask[]>>(generateGlobTasks('*.tmp', {gitignore: true}));
103+
expectType<Promise<GlobTask[]>>(generateGlobTasks('*.tmp', {ignore: ['**/b.tmp']}));
104+
105+
// GenerateGlobTasksSync
106+
expectType<GlobTask[]>(generateGlobTasksSync('*.tmp'));
107+
expectType<GlobTask[]>(generateGlobTasksSync(['a.tmp', '*.tmp', '!{c,d,e}.tmp']));
108+
109+
expectType<GlobTask[]>(generateGlobTasksSync('*.tmp', {expandDirectories: false}));
110+
expectType<GlobTask[]>(
111+
generateGlobTasksSync('*.tmp', {expandDirectories: ['a*', 'b*']}),
112+
);
113+
expectType<GlobTask[]>(
114+
generateGlobTasksSync('*.tmp', {
115+
expandDirectories: {
116+
files: ['a', 'b'],
117+
extensions: ['tmp'],
118+
},
119+
}),
120+
);
121+
expectType<GlobTask[]>(generateGlobTasksSync('*.tmp', {gitignore: true}));
122+
expectType<GlobTask[]>(generateGlobTasksSync('*.tmp', {ignore: ['**/b.tmp']}));
103123

104124
// IsDynamicPattern
105125
expectType<boolean>(isDynamicPattern('**'));

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,10 @@
8181
"ignores": [
8282
"fixtures"
8383
]
84+
},
85+
"ava": {
86+
"files": [
87+
"!tests/utilities.js"
88+
]
8489
}
8590
}

readme.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,16 @@ import {globbyStream} from 'globby';
110110

111111
### generateGlobTasks(patterns, options?)
112112

113-
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.
113+
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.
114114

115115
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.
116116

117+
### generateGlobTasksSync(patterns, options?)
118+
119+
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.
120+
121+
Takes the same arguments as `generateGlobTasks`.
122+
117123
### isDynamicPattern(patterns, options?)
118124

119125
Returns a `boolean` of whether there are any special glob characters in the `patterns`.

tests/generate-glob-tasks.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import util from 'node:util';
2+
import process from 'node:process';
3+
import path from 'node:path';
4+
import test from 'ava';
5+
import {
6+
generateGlobTasks,
7+
generateGlobTasksSync,
8+
} from '../index.js';
9+
import {
10+
invalidPatterns,
11+
getPathValues,
12+
} from './utilities.js';
13+
14+
const runGenerateGlobTasks = async (t, patterns, options) => {
15+
const promiseResult = await generateGlobTasks(patterns, options);
16+
const syncResult = generateGlobTasksSync(patterns, options);
17+
18+
t.deepEqual(
19+
promiseResult,
20+
syncResult,
21+
'generateGlobTasksSync() result is different than generateGlobTasks()',
22+
);
23+
24+
return promiseResult;
25+
};
26+
27+
test('generateGlobTasks', async t => {
28+
const tasks = await runGenerateGlobTasks(t, ['*.tmp', '!b.tmp'], {ignore: ['c.tmp']});
29+
30+
t.is(tasks.length, 1);
31+
t.deepEqual(tasks[0].patterns, ['*.tmp']);
32+
t.deepEqual(tasks[0].options.ignore, ['c.tmp', 'b.tmp']);
33+
await t.notThrowsAsync(generateGlobTasks('*'));
34+
t.notThrows(() => generateGlobTasksSync('*'));
35+
});
36+
37+
// Rejected for being an invalid pattern
38+
for (const value of invalidPatterns) {
39+
const valueString = util.format(value);
40+
const message = 'Patterns must be a string or an array of strings';
41+
42+
test(`throws for invalid patterns input: ${valueString}`, async t => {
43+
await t.throwsAsync(generateGlobTasks(value), {instanceOf: TypeError, message});
44+
t.throws(() => generateGlobTasksSync(value), {instanceOf: TypeError, message});
45+
});
46+
}
47+
48+
test('throws when specifying a file as cwd', async t => {
49+
const error = {message: 'The `cwd` option must be a path to a directory'};
50+
51+
for (const file of getPathValues(path.resolve('fixtures/gitignore/bar.js'))) {
52+
// eslint-disable-next-line no-await-in-loop
53+
await t.throwsAsync(generateGlobTasks('*', {cwd: file}), error);
54+
t.throws(() => generateGlobTasksSync('*', {cwd: file}), error);
55+
}
56+
});
57+
58+
test('cwd', async t => {
59+
const cwd = process.cwd();
60+
for (const cwdDirectory of getPathValues(cwd)) {
61+
// eslint-disable-next-line no-await-in-loop
62+
const [task] = await runGenerateGlobTasks(t, ['*'], {cwd: cwdDirectory});
63+
t.is(task.options.cwd, cwd);
64+
}
65+
});
66+
67+
test('expandDirectories option', async t => {
68+
{
69+
const tasks = await runGenerateGlobTasks(t, ['fixtures'], {ignore: ['fixtures/negative']});
70+
t.is(tasks.length, 1);
71+
t.deepEqual(tasks[0].patterns, ['fixtures/**']);
72+
t.deepEqual(tasks[0].options.ignore, ['fixtures/negative/**']);
73+
}
74+
75+
{
76+
const tasks = await runGenerateGlobTasks(t, ['fixtures'], {ignore: ['fixtures/negative'], expandDirectories: false});
77+
t.is(tasks.length, 1);
78+
t.deepEqual(tasks[0].patterns, ['fixtures']);
79+
t.deepEqual(tasks[0].options.ignore, ['fixtures/negative']);
80+
}
81+
82+
{
83+
const tasks = await runGenerateGlobTasks(t, ['fixtures'], {expandDirectories: ['a*', 'b*']});
84+
t.is(tasks.length, 1);
85+
t.deepEqual(tasks[0].patterns, ['fixtures/**/a*', 'fixtures/**/b*']);
86+
t.deepEqual(tasks[0].options.ignore, []);
87+
}
88+
89+
{
90+
const tasks = await runGenerateGlobTasks(t, ['fixtures'], {
91+
expandDirectories: {
92+
files: ['a', 'b*'],
93+
extensions: ['tmp', 'txt'],
94+
},
95+
ignore: ['**/b.tmp'],
96+
});
97+
t.is(tasks.length, 1);
98+
t.deepEqual(tasks[0].patterns, ['fixtures/**/a.{tmp,txt}', 'fixtures/**/b*.{tmp,txt}']);
99+
t.deepEqual(tasks[0].options.ignore, ['**/b.tmp']);
100+
}
101+
});

0 commit comments

Comments
 (0)