diff --git a/.travis.yml b/.travis.yml index 4735b65..0509c62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,6 @@ os: - windows language: node_js node_js: + - '14' - '12' - '10' diff --git a/gitignore.js b/gitignore.js index 6a1a57a..e85792d 100644 --- a/gitignore.js +++ b/gitignore.js @@ -34,13 +34,15 @@ const parseGitIgnore = (content, options) => { }; const reduceIgnore = files => { - return files.reduce((ignores, file) => { + const ignores = gitIgnore(); + for (const file of files) { ignores.add(parseGitIgnore(file.content, { cwd: file.cwd, fileName: file.filePath })); - return ignores; - }, gitIgnore()); + } + + return ignores; }; const ensureAbsolutePathForCwd = (cwd, p) => { diff --git a/gitignore.test.js b/gitignore.test.js index 72ae0f2..fcc1fa4 100644 --- a/gitignore.test.js +++ b/gitignore.test.js @@ -1,7 +1,7 @@ -import path from 'path'; -import test from 'ava'; -import slash from 'slash'; -import gitignore from './gitignore'; +const path = require('path'); +const test = require('ava'); +const slash = require('slash'); +const gitignore = require('./gitignore'); test('gitignore', async t => { const cwd = path.join(__dirname, 'fixtures/gitignore'); diff --git a/index.d.ts b/index.d.ts index c40e4fe..1e2448b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -43,7 +43,7 @@ declare namespace globby { interface GlobTask { readonly pattern: string; - readonly options: globby.GlobbyOptions; + readonly options: GlobbyOptions; } interface GitignoreOptions { @@ -55,6 +55,11 @@ declare namespace globby { } interface Gitignore { + /** + @returns A filter function indicating whether a given path is ignored via a `.gitignore` file. + */ + sync: (options?: globby.GitignoreOptions) => globby.FilterFunction; + /** `.gitignore` files matched by the ignore config are not used for the resulting filter function. @@ -71,11 +76,6 @@ interface Gitignore { ``` */ (options?: globby.GitignoreOptions): Promise; - - /** - @returns A filter function indicating whether a given path is ignored via a `.gitignore` file. - */ - sync(options?: globby.GitignoreOptions): globby.FilterFunction; } declare const globby: { @@ -84,40 +84,14 @@ declare const globby: { 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. - - @example - ``` - import globby = require('globby'); - - (async () => { - const paths = await globby(['*', '!cake']); - - console.log(paths); - //=> ['unicorn', 'rainbow'] - })(); - ``` - */ - ( - patterns: string | readonly string[], - options?: globby.GlobbyOptions - ): Promise; - - /** - 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. */ - sync( + sync: ( patterns: string | readonly string[], options?: globby.GlobbyOptions - ): string[]; + ) => string[]; /** Find files and directories using glob patterns. @@ -139,10 +113,10 @@ declare const globby: { })(); ``` */ - stream( + stream: ( patterns: string | readonly string[], options?: globby.GlobbyOptions - ): NodeJS.ReadableStream; + ) => NodeJS.ReadableStream; /** 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. @@ -151,10 +125,10 @@ declare const globby: { @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. */ - generateGlobTasks( + generateGlobTasks: ( patterns: string | readonly string[], options?: globby.GlobbyOptions - ): globby.GlobTask[]; + ) => globby.GlobTask[]; /** Note that the options affect the results. @@ -165,12 +139,38 @@ declare const globby: { @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`. */ - hasMagic( + hasMagic: ( patterns: string | readonly string[], options?: FastGlobOptions - ): boolean; + ) => boolean; readonly gitignore: Gitignore; + + /** + 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. + + @example + ``` + import globby = require('globby'); + + (async () => { + const paths = await globby(['*', '!cake']); + + console.log(paths); + //=> ['unicorn', 'rainbow'] + })(); + ``` + */ + ( + patterns: string | readonly string[], + options?: globby.GlobbyOptions + ): Promise; }; export = globby; diff --git a/index.js b/index.js index 3504057..b2d503b 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ const checkCwdOption = (options = {}) => { let stat; try { stat = fs.statSync(options.cwd); - } catch (_) { + } catch { return; } @@ -56,7 +56,7 @@ const generateGlobTasks = (patterns, taskOptions) => { const ignore = patterns .slice(index) - .filter(isNegative) + .filter(pattern => isNegative(pattern)) .map(pattern => pattern.slice(1)); const options = { @@ -138,26 +138,30 @@ module.exports = async (patterns, options) => { module.exports.sync = (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); - const tasks = globTasks.reduce((tasks, task) => { + const tasks = []; + for (const task of globTasks) { const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); + tasks.push(...newTask); + } const filter = getFilterSync(options); - return tasks.reduce( - (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), - [] - ).filter(path_ => !filter(path_)); + let matches = []; + for (const task of tasks) { + matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); + } + + return matches.filter(path_ => !filter(path_)); }; module.exports.stream = (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); - const tasks = globTasks.reduce((tasks, task) => { + const tasks = []; + for (const task of globTasks) { const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); + tasks.push(...newTask); + } const filter = getFilterSync(options); const filterStream = new FilterStream(p => !filter(p)); diff --git a/index.test-d.ts b/index.test-d.ts index 414ce84..2c0dd03 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -68,9 +68,10 @@ expectType(globbyStream('*.tmp', {ignore: ['**/b.tmp']})) for await (const path of globbyStream('*.tmp')) { streamResult.push(path); } + // `NodeJS.ReadableStream` is not generic, unfortunately, // so it seems `(string | Buffer)[]` is the best we can get here - expectType<(string | Buffer)[]>(streamResult); + expectType>(streamResult); })(); // GenerateGlobTasks diff --git a/package.json b/package.json index 11a2270..e7be884 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "author": { "email": "sindresorhus@gmail.com", "name": "Sindre Sorhus", - "url": "sindresorhus.com" + "url": "https://sindresorhus.com" }, "engines": { "node": ">=10" @@ -65,14 +65,14 @@ "slash": "^3.0.0" }, "devDependencies": { - "ava": "^2.1.0", - "get-stream": "^5.1.0", + "ava": "^3.13.0", + "get-stream": "^6.0.0", "glob-stream": "^6.1.0", "globby": "sindresorhus/globby#master", "matcha": "^0.7.0", - "rimraf": "^3.0.0", - "tsd": "^0.11.0", - "xo": "^0.25.3" + "rimraf": "^3.0.2", + "tsd": "^0.13.1", + "xo": "^0.33.1" }, "xo": { "ignores": [ diff --git a/test.js b/test.js index f399e96..6e20df0 100644 --- a/test.js +++ b/test.js @@ -1,12 +1,12 @@ -import fs from 'fs'; -import util from 'util'; -import path from 'path'; -import test from 'ava'; -import getStream from 'get-stream'; -import globby from '.'; +const fs = require('fs'); +const util = require('util'); +const path = require('path'); +const test = require('ava'); +const getStream = require('get-stream'); +const globby = require('.'); const cwd = process.cwd(); -const tmp = 'tmp'; +const temporary = 'tmp'; const fixture = [ 'a.tmp', @@ -17,23 +17,23 @@ const fixture = [ ]; test.before(() => { - if (!fs.existsSync(tmp)) { - fs.mkdirSync(tmp); + if (!fs.existsSync(temporary)) { + fs.mkdirSync(temporary); } for (const element of fixture) { - fs.writeFileSync(element); - fs.writeFileSync(path.join(__dirname, tmp, element)); + fs.writeFileSync(element, ''); + fs.writeFileSync(path.join(__dirname, temporary, element), ''); } }); test.after(() => { for (const element of fixture) { fs.unlinkSync(element); - fs.unlinkSync(path.join(__dirname, tmp, element)); + fs.unlinkSync(path.join(__dirname, temporary, element)); } - fs.rmdirSync(tmp); + fs.rmdirSync(temporary); }); test('glob - async', async t => { @@ -104,7 +104,7 @@ test('return [] for all negative patterns - stream', async t => { }); test('cwd option', t => { - process.chdir(tmp); + process.chdir(temporary); t.deepEqual(globby.sync('*.tmp', {cwd}), ['a.tmp', 'b.tmp', 'c.tmp', 'd.tmp', 'e.tmp']); t.deepEqual(globby.sync(['a.tmp', '*.tmp', '!{c,d,e}.tmp'], {cwd}), ['a.tmp', 'b.tmp']); process.chdir(cwd); @@ -140,16 +140,16 @@ test('expose hasMagic', t => { }); test('expandDirectories option', t => { - t.deepEqual(globby.sync(tmp), ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); - t.deepEqual(globby.sync('**', {cwd: tmp}), ['a.tmp', 'b.tmp', 'c.tmp', 'd.tmp', 'e.tmp']); - t.deepEqual(globby.sync(tmp, {expandDirectories: ['a*', 'b*']}), ['tmp/a.tmp', 'tmp/b.tmp']); - t.deepEqual(globby.sync(tmp, { + t.deepEqual(globby.sync(temporary), ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); + t.deepEqual(globby.sync('**', {cwd: temporary}), ['a.tmp', 'b.tmp', 'c.tmp', 'd.tmp', 'e.tmp']); + t.deepEqual(globby.sync(temporary, {expandDirectories: ['a*', 'b*']}), ['tmp/a.tmp', 'tmp/b.tmp']); + t.deepEqual(globby.sync(temporary, { expandDirectories: { files: ['a', 'b'], extensions: ['tmp'] } }), ['tmp/a.tmp', 'tmp/b.tmp']); - t.deepEqual(globby.sync(tmp, { + t.deepEqual(globby.sync(temporary, { expandDirectories: { files: ['a', 'b'], extensions: ['tmp'] @@ -159,14 +159,14 @@ test('expandDirectories option', t => { }); test('expandDirectories:true and onlyFiles:true option', t => { - t.deepEqual(globby.sync(tmp, {onlyFiles: true}), ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); + t.deepEqual(globby.sync(temporary, {onlyFiles: true}), ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); }); test.failing('expandDirectories:true and onlyFiles:false option', t => { // Node-glob('tmp/**') => ['tmp', 'tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp'] // Fast-glob('tmp/**') => ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp'] // See https://github.com/mrmlnc/fast-glob/issues/47 - t.deepEqual(globby.sync(tmp, {onlyFiles: false}), ['tmp', 'tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); + t.deepEqual(globby.sync(temporary, {onlyFiles: false}), ['tmp', 'tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); }); test('expandDirectories and ignores option', t => { @@ -181,7 +181,7 @@ test('expandDirectories and ignores option', t => { }); test.failing('relative paths and ignores option', t => { - process.chdir(tmp); + process.chdir(temporary); t.deepEqual(globby.sync('../tmp', { cwd: process.cwd(), ignore: ['tmp'] @@ -201,8 +201,8 @@ test.failing('relative paths and ignores option', t => { [null], undefined, [undefined], - NaN, - [NaN], + Number.NaN, + [Number.NaN], 5, [5], function () {}, @@ -212,38 +212,38 @@ test.failing('relative paths and ignores option', t => { const message = 'Patterns must be a string or an array of strings'; test(`rejects the promise for invalid patterns input: ${valueString} - async`, async t => { - await t.throwsAsync(globby(value), TypeError); - await t.throwsAsync(globby(value), message); + await t.throwsAsync(globby(value), {instanceOf: TypeError}); + await t.throwsAsync(globby(value), {message}); }); test(`throws for invalid patterns input: ${valueString} - sync`, t => { t.throws(() => { globby.sync(value); - }, TypeError); + }, {instanceOf: TypeError}); t.throws(() => { globby.sync(value); - }, message); + }, {message}); }); test(`throws for invalid patterns input: ${valueString} - stream`, t => { t.throws(() => { globby.stream(value); - }, TypeError); + }, {instanceOf: TypeError}); t.throws(() => { globby.stream(value); - }, message); + }, {message}); }); test(`generateGlobTasks throws for invalid patterns input: ${valueString}`, t => { t.throws(() => { globby.generateGlobTasks(value); - }, TypeError); + }, {instanceOf: TypeError}); t.throws(() => { globby.generateGlobTasks(value); - }, message); + }, {message}); }); }); @@ -306,7 +306,7 @@ test('respects gitignore option false - stream', async t => { test('`{extension: false}` and `expandDirectories.extensions` option', t => { t.deepEqual( globby.sync('*', { - cwd: tmp, + cwd: temporary, extension: false, expandDirectories: { extensions: [ @@ -330,12 +330,12 @@ test('throws when specifying a file as cwd - async', async t => { await t.throwsAsync( globby('.', {cwd: isFile}), - 'The `cwd` option must be a path to a directory' + {message: 'The `cwd` option must be a path to a directory'} ); await t.throwsAsync( globby('*', {cwd: isFile}), - 'The `cwd` option must be a path to a directory' + {message: 'The `cwd` option must be a path to a directory'} ); }); @@ -344,11 +344,11 @@ test('throws when specifying a file as cwd - sync', t => { t.throws(() => { globby.sync('.', {cwd: isFile}); - }, 'The `cwd` option must be a path to a directory'); + }, {message: 'The `cwd` option must be a path to a directory'}); t.throws(() => { globby.sync('*', {cwd: isFile}); - }, 'The `cwd` option must be a path to a directory'); + }, {message: 'The `cwd` option must be a path to a directory'}); }); test('throws when specifying a file as cwd - stream', t => { @@ -356,11 +356,11 @@ test('throws when specifying a file as cwd - stream', t => { t.throws(() => { globby.stream('.', {cwd: isFile}); - }, 'The `cwd` option must be a path to a directory'); + }, {message: 'The `cwd` option must be a path to a directory'}); t.throws(() => { globby.stream('*', {cwd: isFile}); - }, 'The `cwd` option must be a path to a directory'); + }, {message: 'The `cwd` option must be a path to a directory'}); }); test('don\'t throw when specifying a non-existing cwd directory - async', async t => {