From 0ecfbb190f665690d38fbfdaa63a57d04ce02d34 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Fri, 16 Jul 2021 23:19:12 +0300 Subject: [PATCH] feat: move to esm BREAKING CHANGE: require Node.js >= 12.20 --- .github/workflows/main.yml | 4 +-- bench.js | 65 ++++++++++++++++++---------------- gitignore.js | 45 ++++++++++++------------ gitignore.test.js | 15 ++++---- index.d.ts | 2 ++ index.js | 72 +++++++++++++++++++++----------------- index.test-d.ts | 50 +++++++++++++------------- package.json | 24 +++++++------ stream-utils.js | 9 +++-- test.js | 54 ++++++++++++++-------------- 10 files changed, 181 insertions(+), 159 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3488385..495ba52 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,16 +10,16 @@ jobs: fail-fast: false matrix: node-version: + - 16 - 14 - 12 - - 10 os: - ubuntu-latest - macos-latest - windows-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/bench.js b/bench.js index 339a5c9..6f6c561 100644 --- a/bench.js +++ b/bench.js @@ -1,12 +1,14 @@ -'use strict'; /* global after, before, bench, suite */ -const fs = require('fs'); -const rimraf = require('rimraf'); -const globbyMainBranch = require('globby'); -const gs = require('glob-stream'); -const fastGlob = require('fast-glob'); -const globby = require('.'); +import fs from 'node:fs'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import rimraf from 'rimraf'; +import globbyMainBranch from 'globby'; +import gs from 'glob-stream'; +import fastGlob from 'fast-glob'; +import globby from './index.js'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const BENCH_DIR = 'bench'; const runners = [{ @@ -14,58 +16,58 @@ const runners = [{ run: async (patterns, callback) => { await globby(patterns); callback(); - } + }, }, { name: 'globby async (upstream/main)', run: async (patterns, callback) => { await globbyMainBranch(patterns); callback(); - } + }, }, { name: 'globby sync (working directory)', run: patterns => { globby.sync(patterns); - } + }, }, { name: 'globby sync (upstream/main)', run: patterns => { globbyMainBranch.sync(patterns); - } + }, }, { name: 'glob-stream', run: (patterns, cb) => { gs(patterns).on('data', () => {}).on('end', cb); - } + }, }, { name: 'fast-glob async', run: async (patterns, callback) => { await fastGlob(patterns); callback(); - } + }, }, { name: 'fast-glob sync', run: patterns => { fastGlob.sync(patterns); - } + }, }]; const benchs = [{ name: 'negative globs (some files inside dir)', patterns: [ 'a/*', - '!a/c*' - ] + '!a/c*', + ], }, { name: 'negative globs (whole dir)', patterns: [ 'a/*', - '!a/**' - ] + '!a/**', + ], }, { name: 'multiple positive globs', patterns: [ 'a/*', - 'b/*' - ] + 'b/*', + ], }]; before(() => { @@ -73,14 +75,13 @@ before(() => { rimraf.sync(BENCH_DIR); fs.mkdirSync(BENCH_DIR); process.chdir(BENCH_DIR); - ['a', 'b'] - .map(directory => `${directory}/`) - .forEach(directory => { - fs.mkdirSync(directory); - for (let i = 0; i < 500; i++) { - fs.writeFileSync(directory + (i < 100 ? 'c' : 'd') + i, ''); - } - }); + for (const directory of ['a', 'b'] + .map(directory => `${directory}/`)) { + fs.mkdirSync(directory); + for (let i = 0; i < 500; i++) { + fs.writeFileSync(directory + (i < 100 ? 'c' : 'd') + i, ''); + } + } }); after(() => { @@ -88,8 +89,10 @@ after(() => { rimraf.sync(BENCH_DIR); }); -benchs.forEach(benchmark => { +for (const benchmark of benchs) { suite(benchmark.name, () => { - runners.forEach(runner => bench(runner.name, runner.run.bind(null, benchmark.patterns))); + for (const runner of runners) { + bench(runner.name, runner.run.bind(null, benchmark.patterns)); + } }); -}); +} diff --git a/gitignore.js b/gitignore.js index 2f77baa..5047ac6 100644 --- a/gitignore.js +++ b/gitignore.js @@ -1,16 +1,15 @@ -'use strict'; -const {promisify} = require('util'); -const fs = require('fs'); -const path = require('path'); -const fastGlob = require('fast-glob'); -const gitIgnore = require('ignore'); -const slash = require('slash'); +import {promisify} from 'node:util'; +import fs from 'node:fs'; +import path from 'node:path'; +import fastGlob from 'fast-glob'; +import gitIgnore from 'ignore'; +import slash from 'slash'; const DEFAULT_IGNORE = [ '**/node_modules/**', '**/flow-typed/**', '**/coverage/**', - '**/.git' + '**/.git', ]; const readFileP = promisify(fs.readFile); @@ -38,7 +37,7 @@ const reduceIgnore = files => { for (const file of files) { ignores.add(parseGitIgnore(file.content, { cwd: file.cwd, - fileName: file.filePath + fileName: file.filePath, })); } @@ -58,9 +57,7 @@ const ensureAbsolutePathForCwd = (cwd, p) => { return path.join(cwd, p); }; -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p)))); -}; +const getIsIgnoredPredecate = (ignores, cwd) => p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p)))); const getFile = async (file, cwd) => { const filePath = path.join(cwd, file); @@ -69,7 +66,7 @@ const getFile = async (file, cwd) => { return { cwd, filePath, - content + content, }; }; @@ -80,23 +77,21 @@ const getFileSync = (file, cwd) => { return { cwd, filePath, - content + content, }; }; const normalizeOptions = ({ ignore = [], - cwd = slash(process.cwd()) -} = {}) => { - return {ignore, cwd}; -}; + cwd = slash(process.cwd()), +} = {}) => ({ignore, cwd}); -module.exports = async options => { +export const gitignore = async options => { options = normalizeOptions(options); const paths = await fastGlob('**/.gitignore', { ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd + cwd: options.cwd, }); const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); @@ -105,12 +100,12 @@ module.exports = async options => { return getIsIgnoredPredecate(ignores, options.cwd); }; -module.exports.sync = options => { +export const gitignoreSync = options => { options = normalizeOptions(options); const paths = fastGlob.sync('**/.gitignore', { ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd + cwd: options.cwd, }); const files = paths.map(file => getFileSync(file, options.cwd)); @@ -118,3 +113,9 @@ module.exports.sync = options => { return getIsIgnoredPredecate(ignores, options.cwd); }; + +// Legacy API +gitignore.sync = gitignoreSync; + +export default gitignore; + diff --git a/gitignore.test.js b/gitignore.test.js index fd25b31..9b6a110 100644 --- a/gitignore.test.js +++ b/gitignore.test.js @@ -1,7 +1,10 @@ -const path = require('path'); -const test = require('ava'); -const slash = require('slash'); -const gitignore = require('./gitignore'); +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import test from 'ava'; +import slash from 'slash'; +import {gitignore} from './gitignore.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); test('gitignore', async t => { const cwd = path.join(__dirname, 'fixtures/gitignore'); @@ -75,7 +78,7 @@ test('multiple negation', async t => { '!!!unicorn.js', '!!unicorn.js', '!unicorn.js', - 'unicorn.js' + 'unicorn.js', ].filter(file => !isIgnored(file)); const expected = ['!!unicorn.js', '!unicorn.js']; @@ -90,7 +93,7 @@ test('multiple negation - sync', t => { '!!!unicorn.js', '!!unicorn.js', '!unicorn.js', - 'unicorn.js' + 'unicorn.js', ].filter(file => !isIgnored(file)); const expected = ['!!unicorn.js', '!unicorn.js']; diff --git a/index.d.ts b/index.d.ts index 2e563fc..9806ccc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/member-ordering, @typescript-eslint/no-redeclare */ + import {Options as FastGlobOptions, Entry as FastGlobEntry} from 'fast-glob'; declare namespace globby { diff --git a/index.js b/index.js index b2d503b..0b3039f 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,10 @@ -'use strict'; -const fs = require('fs'); -const arrayUnion = require('array-union'); -const merge2 = require('merge2'); -const fastGlob = require('fast-glob'); -const dirGlob = require('dir-glob'); -const gitignore = require('./gitignore'); -const {FilterStream, UniqueStream} = require('./stream-utils'); +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 {gitignore, gitignoreSync} from './gitignore.js'; +import {FilterStream, UniqueStream} from './stream-utils.js'; const DEFAULT_FILTER = () => false; @@ -36,8 +35,8 @@ const checkCwdOption = (options = {}) => { const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); +export const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([patterns].flat()); assertPatternsInput(patterns); checkCwdOption(taskOptions); @@ -46,7 +45,7 @@ const generateGlobTasks = (patterns, taskOptions) => { taskOptions = { ignore: [], expandDirectories: true, - ...taskOptions + ...taskOptions, }; for (const [index, pattern] of patterns.entries()) { @@ -61,7 +60,7 @@ const generateGlobTasks = (patterns, taskOptions) => { const options = { ...taskOptions, - ignore: taskOptions.ignore.concat(ignore) + ignore: [...taskOptions.ignore, ...ignore], }; globTasks.push({pattern, options}); @@ -79,12 +78,12 @@ const globDirs = (task, fn) => { if (Array.isArray(task.options.expandDirectories)) { options = { ...options, - files: task.options.expandDirectories + files: task.options.expandDirectories, }; } else if (typeof task.options.expandDirectories === 'object') { options = { ...options, - ...task.options.expandDirectories + ...task.options.expandDirectories, }; } @@ -93,11 +92,9 @@ const globDirs = (task, fn) => { const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -const getFilterSync = options => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; -}; +const getFilterSync = options => options && options.gitignore + ? gitignoreSync({cwd: options.cwd, ignore: options.ignore}) + : DEFAULT_FILTER; const globToTask = task => glob => { const {options} = task; @@ -107,18 +104,16 @@ const globToTask = task => glob => { return { pattern: glob, - options + options, }; }; -module.exports = async (patterns, options) => { +export const globby = async (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); - const getFilter = async () => { - return options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; + const getFilter = async () => options && options.gitignore + ? gitignore({cwd: options.cwd, ignore: options.ignore}) + : DEFAULT_FILTER; const getTasks = async () => { const tasks = await Promise.all(globTasks.map(async task => { @@ -135,7 +130,7 @@ module.exports = async (patterns, options) => { return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); }; -module.exports.sync = (patterns, options) => { +export const sync = (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); const tasks = []; @@ -154,7 +149,7 @@ module.exports.sync = (patterns, options) => { return matches.filter(path_ => !filter(path_)); }; -module.exports.stream = (patterns, options) => { +export const stream = (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); const tasks = []; @@ -172,10 +167,21 @@ module.exports.stream = (patterns, options) => { .pipe(uniqueStream); }; -module.exports.generateGlobTasks = generateGlobTasks; - -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) +export const hasMagic = (patterns, options) => [patterns].flat() .some(pattern => fastGlob.isDynamicPattern(pattern, options)); -module.exports.gitignore = gitignore; +export { + gitignore, + gitignoreSync, +}; + +// Legacy API +Object.assign(globby, { + hasMagic, + generateGlobTasks, + gitignore, + stream, + sync, +}); + +export default globby; diff --git a/index.test-d.ts b/index.test-d.ts index 3681c25..2df7c08 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,5 +1,7 @@ +/* eslint-disable @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/no-unsafe-member-access */ + import {expectType} from 'tsd'; -import globby = require('.'); +import globby = require('./index.js'); import { GlobTask, FilterFunction, @@ -7,7 +9,7 @@ import { stream as globbyStream, generateGlobTasks, hasMagic, - gitignore + gitignore, } from '.'; // Globby @@ -16,15 +18,15 @@ expectType>(globby(['a.tmp', '*.tmp', '!{c,d,e}.tmp'])); expectType>(globby('*.tmp', {expandDirectories: false})); expectType>( - globby('*.tmp', {expandDirectories: ['a*', 'b*']}) + globby('*.tmp', {expandDirectories: ['a*', 'b*']}), ); expectType>( globby('*.tmp', { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] - } - }) + extensions: ['tmp'], + }, + }), ); expectType>(globby('*.tmp', {gitignore: true})); expectType>(globby('*.tmp', {ignore: ['**/b.tmp']})); @@ -40,9 +42,9 @@ expectType( globbySync('*.tmp', { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] - } - }) + extensions: ['tmp'], + }, + }), ); expectType(globbySync('*.tmp', {gitignore: true})); expectType(globbySync('*.tmp', {ignore: ['**/b.tmp']})); @@ -58,9 +60,9 @@ expectType( globbyStream('*.tmp', { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] - } - }) + extensions: ['tmp'], + }, + }), ); expectType(globbyStream('*.tmp', {gitignore: true})); expectType(globbyStream('*.tmp', {ignore: ['**/b.tmp']})); @@ -82,15 +84,15 @@ expectType(generateGlobTasks(['a.tmp', '*.tmp', '!{c,d,e}.tmp'])); expectType(generateGlobTasks('*.tmp', {expandDirectories: false})); expectType( - generateGlobTasks('*.tmp', {expandDirectories: ['a*', 'b*']}) + generateGlobTasks('*.tmp', {expandDirectories: ['a*', 'b*']}), ); expectType( generateGlobTasks('*.tmp', { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] - } - }) + extensions: ['tmp'], + }, + }), ); expectType(generateGlobTasks('*.tmp', {gitignore: true})); expectType(generateGlobTasks('*.tmp', {ignore: ['**/b.tmp']})); @@ -104,24 +106,24 @@ expectType(hasMagic(['**', 'path1', 'path2'], {extglob: false})); expectType>(gitignore()); expectType>( gitignore({ - cwd: __dirname - }) + cwd: __dirname as string, + }), ); expectType>( gitignore({ - ignore: ['**/b.tmp'] - }) + ignore: ['**/b.tmp'], + }), ); // Gitignore (sync) expectType(gitignore.sync()); expectType( gitignore.sync({ - cwd: __dirname - }) + cwd: __dirname as string, + }), ); expectType( gitignore.sync({ - ignore: ['**/b.tmp'] - }) + ignore: ['**/b.tmp'], + }), ); diff --git a/package.json b/package.json index 4970eb0..6f70385 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "url": "https://sindresorhus.com" }, "engines": { - "node": ">=10" + "node": ">=12.20" }, + "type": "module", + "exports": "./index.js", "scripts": { "bench": "npm update glob-stream fast-glob && matcha bench.js", "test": "xo && ava && tsd" @@ -57,22 +59,24 @@ "git" ], "dependencies": { - "array-union": "^2.1.0", + "array-union": "^3.0.1", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "fast-glob": "^3.2.7", + "ignore": "^5.1.8", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, "devDependencies": { - "ava": "^3.13.0", - "get-stream": "^6.0.0", + "@types/node": "^16.3.3", + "ava": "^3.15.0", + "get-stream": "^6.0.1", "glob-stream": "^6.1.0", "globby": "sindresorhus/globby#main", "matcha": "^0.7.0", "rimraf": "^3.0.2", - "tsd": "^0.13.1", - "xo": "^0.33.1" + "typescript": "^4.3.5", + "tsd": "^0.17.0", + "xo": "^0.42.0" }, "xo": { "ignores": [ diff --git a/stream-utils.js b/stream-utils.js index 98aedc8..3e432b7 100644 --- a/stream-utils.js +++ b/stream-utils.js @@ -1,10 +1,9 @@ -'use strict'; -const {Transform} = require('stream'); +import {Transform} from 'node:stream'; class ObjectTransform extends Transform { constructor() { super({ - objectMode: true + objectMode: true, }); } } @@ -40,7 +39,7 @@ class UniqueStream extends ObjectTransform { } } -module.exports = { +export { FilterStream, - UniqueStream + UniqueStream, }; diff --git a/test.js b/test.js index c73fbdb..1fc9e5d 100644 --- a/test.js +++ b/test.js @@ -1,10 +1,12 @@ -const fs = require('fs'); -const util = require('util'); -const path = require('path'); -const test = require('ava'); -const getStream = require('get-stream'); -const globby = require('.'); - +import fs from 'node:fs'; +import path from 'node:path'; +import util from 'node:util'; +import {fileURLToPath} from 'node:url'; +import test from 'ava'; +import getStream from 'get-stream'; +import globby from './index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const cwd = process.cwd(); const temporary = 'tmp'; @@ -13,7 +15,7 @@ const fixture = [ 'b.tmp', 'c.tmp', 'd.tmp', - 'e.tmp' + 'e.tmp', ]; test.before(() => { @@ -146,15 +148,15 @@ test('expandDirectories option', t => { t.deepEqual(globby.sync(temporary, { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] - } + extensions: ['tmp'], + }, }), ['tmp/a.tmp', 'tmp/b.tmp']); t.deepEqual(globby.sync(temporary, { expandDirectories: { files: ['a', 'b'], - extensions: ['tmp'] + extensions: ['tmp'], }, - ignore: ['**/b.tmp'] + ignore: ['**/b.tmp'], }), ['tmp/a.tmp']); }); @@ -171,12 +173,12 @@ test.failing('expandDirectories:true and onlyFiles:false option', t => { test('expandDirectories and ignores option', t => { t.deepEqual(globby.sync('tmp', { - ignore: ['tmp'] + ignore: ['tmp'], }), []); t.deepEqual(globby.sync('tmp/**', { expandDirectories: false, - ignore: ['tmp'] + ignore: ['tmp'], }), ['tmp/a.tmp', 'tmp/b.tmp', 'tmp/c.tmp', 'tmp/d.tmp', 'tmp/e.tmp']); }); @@ -184,13 +186,13 @@ test.failing('relative paths and ignores option', t => { process.chdir(temporary); t.deepEqual(globby.sync('../tmp', { cwd: process.cwd(), - ignore: ['tmp'] + ignore: ['tmp'], }), []); process.chdir(cwd); }); // Rejected for being an invalid pattern -[ +for (const value of [ {}, [{}], true, @@ -206,8 +208,8 @@ test.failing('relative paths and ignores option', t => { 5, [5], function () {}, - [function () {}] -].forEach(value => { + [function () {}], +]) { const valueString = util.format(value); const message = 'Patterns must be a string or an array of strings'; @@ -245,7 +247,7 @@ test.failing('relative paths and ignores option', t => { globby.generateGlobTasks(value); }, {message}); }); -}); +} test('gitignore option defaults to false - async', async t => { const actual = await globby('*', {onlyFiles: false}); @@ -323,17 +325,17 @@ test('`{extension: false}` and `expandDirectories.extensions` option', t => { expandDirectories: { extensions: [ 'md', - 'tmp' - ] - } + 'tmp', + ], + }, }), [ 'a.tmp', 'b.tmp', 'c.tmp', 'd.tmp', - 'e.tmp' - ] + 'e.tmp', + ], ); }); @@ -342,12 +344,12 @@ test('throws when specifying a file as cwd - async', async t => { await t.throwsAsync( globby('.', {cwd: isFile}), - {message: '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}), - {message: 'The `cwd` option must be a path to a directory'} + {message: 'The `cwd` option must be a path to a directory'}, ); });