pax_global_header 0000666 0000000 0000000 00000000064 14306550000 0014503 g ustar 00root root 0000000 0000000 52 comment=a028e3670ba3186a102cb9108e3a6e48c1148a9e
fast-glob-3.2.12/ 0000775 0000000 0000000 00000000000 14306550000 0013446 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/.editorconfig 0000664 0000000 0000000 00000000532 14306550000 0016123 0 ustar 00root root 0000000 0000000 # editorconfig.org
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{.travis.yml,circle.yml,appveyor.yml,.github/**}]
indent_style = space
indent_size = 2
[{npm-shrinkwrap.json,package-lock.json,package.json}]
indent_style = space
indent_size = 2
fast-glob-3.2.12/.eslintrc.json 0000664 0000000 0000000 00000000165 14306550000 0016244 0 ustar 00root root 0000000 0000000 {
"extends": "mrmlnc",
"rules": {
"no-magic-numbers": "off",
"@typescript-eslint/no-magic-numbers": "off"
}
}
fast-glob-3.2.12/.gitattributes 0000664 0000000 0000000 00000000023 14306550000 0016334 0 ustar 00root root 0000000 0000000 * text=auto eol=lf
fast-glob-3.2.12/.github/ 0000775 0000000 0000000 00000000000 14306550000 0015006 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/.github/CODE-OF-CONDUCT.md 0000664 0000000 0000000 00000006175 14306550000 0017452 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at dmalinochkin@rambler.ru. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
fast-glob-3.2.12/.github/CONTRIBUTING.md 0000664 0000000 0000000 00000003216 14306550000 0017241 0 ustar 00root root 0000000 0000000 # Contributing to my package
Welcome, and thank you for your interest in contributing to **fast-glob**!
Please note that this project is released with a [Contributor Code of Conduct](CODE-OF-CONDUCT.md). By participating in this project you agree to abide by its terms.
## Contribution Guidelines
There are a couple of ways you can contribute to this repository:
* **Ideas, feature requests and bugs**: We are open to all ideas and we want to get rid of bugs! Use the [Issues section](https://github.com/mrmlnc/fast-glob/issues) to either report a new issue, provide your ideas or contribute to existing threads.
* **Documentation**: Found a typo or strangely worded sentences? Submit a PR!
* **Code**: Contribute bug fixes, features or design changes.
### Creating an Issue
Before you create a new Issue:
* Check the [Issues](https://github.com/mrmlnc/fast-glob/issues) on GitHub to ensure one doesn't already exist.
* Clearly describe the issue, including the steps to reproduce the issue.
### Making Changes
#### Getting Started
* Install [Node.js](https://nodejs.org/en/).
* Fork the project and clone the fork repository. ([how to create fork?](https://help.github.com/articles/fork-a-repo/#fork-an-example-repository)).
* Create a topic branch from the master branch.
* Run `yarn` or `npm install` to install the application dependencies.
#### Setup
1. Start watching:
```
$ npm run watch
```
2. Make changes:
```
$ code .
```
3. Run tests:
```
$ npm t
```
#### Commit
Keep git commit messages clear and appropriate. You can use [Angular guide](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines).
fast-glob-3.2.12/.github/FUNDING.yml 0000664 0000000 0000000 00000000137 14306550000 0016624 0 ustar 00root root 0000000 0000000 ko_fi: mrmlnc
custom:
- https://www.buymeacoffee.com/mrmlnc
- https://www.paypal.me/mrmlnc
fast-glob-3.2.12/.github/ISSUE_TEMPLATE.md 0000664 0000000 0000000 00000000311 14306550000 0017506 0 ustar 00root root 0000000 0000000 ### Environment
* OS Version: …
* Node.js Version: …
### Actual behavior
…
### Expected behavior
…
### Steps to reproduce
1. …
### Code sample
```js
// Paste your code here.
```
fast-glob-3.2.12/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000000151 14306550000 0020604 0 ustar 00root root 0000000 0000000 ### What is the purpose of this pull request?
…
### What changes did you make? (Give an overview)
…
fast-glob-3.2.12/.github/workflows/ 0000775 0000000 0000000 00000000000 14306550000 0017043 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/.github/workflows/benchmark.yml 0000664 0000000 0000000 00000003743 14306550000 0021527 0 ustar 00root root 0000000 0000000 name: Benchmark
on:
- push
env:
BENCHMARK_LAUNCHES: 100
BENCHMARK_MAX_STDEV: 10
BENCHMARK_RETRIES: 3
jobs:
cancel:
name: Cancel previous jobs
if: github.ref != 'refs/heads/master'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: Cancel previous jobs
uses: styfle/cancel-workflow-action@0.6.0
with:
workflow_id: 4198440
access_token: ${{ github.token }}
product:
name: Product benchmark
if: always()
needs:
- cancel
runs-on: ubuntu-latest
env:
BENCHMARK_TYPE: product
BENCHMARK_OPTIONS: '{}'
steps:
- name: Setup repository
uses: actions/checkout@v2
- name: Setup environment
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install dependencies
run: npm install
- name: Compile sources
run: npm run compile
- name: Benchmark (async)
run: npm run bench-async
- name: Benchmark (stream)
run: npm run bench-stream
- name: Benchmark (sync)
run: npm run bench-sync
regress:
name: Regress benchmark with options (${{ matrix.benchmark_options }})
needs:
- cancel
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
benchmark_options:
- '{}'
- '{ "objectMode": true }'
- '{ "absolute": true }'
env:
BENCHMARK_TYPE: regression
BENCHMARK_OPTIONS: ${{ matrix.benchmark_options }}
steps:
- name: Setup repository
uses: actions/checkout@v2
- name: Setup environment
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install dependencies
run: npm install
- name: Compile sources
run: npm run compile
- name: Benchmark (async)
run: npm run bench-async
- name: Benchmark (stream)
run: npm run bench-stream
- name: Benchmark (sync)
run: npm run bench-sync
fast-glob-3.2.12/.github/workflows/codeql.yml 0000664 0000000 0000000 00000001157 14306550000 0021041 0 ustar 00root root 0000000 0000000 name: "CodeQL"
on:
push:
branches:
- master
- "!dependabot/**"
pull_request:
branches:
- master
schedule:
- cron: "0 0 * * 0"
workflow_dispatch:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: "javascript"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
fast-glob-3.2.12/.github/workflows/main.yml 0000664 0000000 0000000 00000002616 14306550000 0020517 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches:
- master
- releases/*
pull_request:
branches:
- '*'
jobs:
cancel:
name: Cancel previous jobs
if: github.ref != 'refs/heads/master'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: Cancel previous jobs
uses: styfle/cancel-workflow-action@0.6.0
with:
workflow_id: 4197767
access_token: ${{ github.token }}
test:
name: Node.js ${{ matrix.node_version }} on ${{ matrix.os }}
if: always()
needs:
- cancel
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node_version: [8, 10, 12, 14, 16]
os:
- ubuntu-latest
- macos-latest
- windows-latest
steps:
- name: Setup repository
uses: actions/checkout@v2
- name: Setup environment
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node_version }}
- name: Install dependencies
run: npm install
- name: Compile sources
run: npm run compile
- name: Run Hygiene Checks
run: npm run lint
- name: Run unit tests
run: npm run test
- name: Run smoke tests (sync)
run: npm run smoke:sync
- name: Run smoke tests (async)
run: npm run smoke:async
- name: Run smoke tests (stream)
run: npm run smoke:stream
fast-glob-3.2.12/.gitignore 0000664 0000000 0000000 00000000326 14306550000 0015437 0 ustar 00root root 0000000 0000000 # Logs
logs/
*.log
npm-debug.log*
# IDE & editors
.idea
.vscode
# Dependency directory
node_modules/
# Compiled and temporary files
.eslintcache
.tmp/
.benchmark/
out/
# Other files
package-lock.json
yarn.lock
fast-glob-3.2.12/.npmrc 0000664 0000000 0000000 00000000023 14306550000 0014561 0 ustar 00root root 0000000 0000000 package-lock=false
fast-glob-3.2.12/LICENSE 0000664 0000000 0000000 00000002067 14306550000 0014460 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) Denis Malinochkin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
fast-glob-3.2.12/README.md 0000664 0000000 0000000 00000056136 14306550000 0014740 0 ustar 00root root 0000000 0000000 # fast-glob
> It's a very fast and efficient [glob][glob_definition] library for [Node.js][node_js].
This package provides methods for traversing the file system and returning pathnames that matched a defined set of a specified pattern according to the rules used by the Unix Bash shell with some simplifications, meanwhile results are returned in **arbitrary order**. Quick, simple, effective.
## Table of Contents
Details
* [Highlights](#highlights)
* [Donation](#donation)
* [Old and modern mode](#old-and-modern-mode)
* [Pattern syntax](#pattern-syntax)
* [Basic syntax](#basic-syntax)
* [Advanced syntax](#advanced-syntax)
* [Installation](#installation)
* [API](#api)
* [Asynchronous](#asynchronous)
* [Synchronous](#synchronous)
* [Stream](#stream)
* [patterns](#patterns)
* [[options]](#options)
* [Helpers](#helpers)
* [generateTasks](#generatetaskspatterns-options)
* [isDynamicPattern](#isdynamicpatternpattern-options)
* [escapePath](#escapepathpattern)
* [Options](#options-3)
* [Common](#common)
* [concurrency](#concurrency)
* [cwd](#cwd)
* [deep](#deep)
* [followSymbolicLinks](#followsymboliclinks)
* [fs](#fs)
* [ignore](#ignore)
* [suppressErrors](#suppresserrors)
* [throwErrorOnBrokenSymbolicLink](#throwerroronbrokensymboliclink)
* [Output control](#output-control)
* [absolute](#absolute)
* [markDirectories](#markdirectories)
* [objectMode](#objectmode)
* [onlyDirectories](#onlydirectories)
* [onlyFiles](#onlyfiles)
* [stats](#stats)
* [unique](#unique)
* [Matching control](#matching-control)
* [braceExpansion](#braceexpansion)
* [caseSensitiveMatch](#casesensitivematch)
* [dot](#dot)
* [extglob](#extglob)
* [globstar](#globstar)
* [baseNameMatch](#basenamematch)
* [FAQ](#faq)
* [What is a static or dynamic pattern?](#what-is-a-static-or-dynamic-pattern)
* [How to write patterns on Windows?](#how-to-write-patterns-on-windows)
* [Why are parentheses match wrong?](#why-are-parentheses-match-wrong)
* [How to exclude directory from reading?](#how-to-exclude-directory-from-reading)
* [How to use UNC path?](#how-to-use-unc-path)
* [Compatible with `node-glob`?](#compatible-with-node-glob)
* [Benchmarks](#benchmarks)
* [Server](#server)
* [Nettop](#nettop)
* [Changelog](#changelog)
* [License](#license)
## Highlights
* Fast. Probably the fastest.
* Supports multiple and negative patterns.
* Synchronous, Promise and Stream API.
* Object mode. Can return more than just strings.
* Error-tolerant.
## Donation
Do you like this project? Support it by donating, creating an issue or pull request.
[][paypal_mrmlnc]
## Old and modern mode
This package works in two modes, depending on the environment in which it is used.
* **Old mode**. Node.js below 10.10 or when the [`stats`](#stats) option is *enabled*.
* **Modern mode**. Node.js 10.10+ and the [`stats`](#stats) option is *disabled*.
The modern mode is faster. Learn more about the [internal mechanism][nodelib_fs_scandir_old_and_modern_modern].
## Pattern syntax
> :warning: Always use forward-slashes in glob expressions (patterns and [`ignore`](#ignore) option). Use backslashes for escaping characters.
There is more than one form of syntax: basic and advanced. Below is a brief overview of the supported features. Also pay attention to our [FAQ](#faq).
> :book: This package uses a [`micromatch`][micromatch] as a library for pattern matching.
### Basic syntax
* An asterisk (`*`) — matches everything except slashes (path separators), hidden files (names starting with `.`).
* A double star or globstar (`**`) — matches zero or more directories.
* Question mark (`?`) – matches any single character except slashes (path separators).
* Sequence (`[seq]`) — matches any character in sequence.
> :book: A few additional words about the [basic matching behavior][picomatch_matching_behavior].
Some examples:
* `src/**/*.js` — matches all files in the `src` directory (any level of nesting) that have the `.js` extension.
* `src/*.??` — matches all files in the `src` directory (only first level of nesting) that have a two-character extension.
* `file-[01].js` — matches files: `file-0.js`, `file-1.js`.
### Advanced syntax
* [Escapes characters][micromatch_backslashes] (`\\`) — matching special characters (`$^*+?()[]`) as literals.
* [POSIX character classes][picomatch_posix_brackets] (`[[:digit:]]`).
* [Extended globs][micromatch_extglobs] (`?(pattern-list)`).
* [Bash style brace expansions][micromatch_braces] (`{}`).
* [Regexp character classes][micromatch_regex_character_classes] (`[1-5]`).
* [Regex groups][regular_expressions_brackets] (`(a|b)`).
> :book: A few additional words about the [advanced matching behavior][micromatch_extended_globbing].
Some examples:
* `src/**/*.{css,scss}` — matches all files in the `src` directory (any level of nesting) that have the `.css` or `.scss` extension.
* `file-[[:digit:]].js` — matches files: `file-0.js`, `file-1.js`, …, `file-9.js`.
* `file-{1..3}.js` — matches files: `file-1.js`, `file-2.js`, `file-3.js`.
* `file-(1|2)` — matches files: `file-1.js`, `file-2.js`.
## Installation
```console
npm install fast-glob
```
## API
### Asynchronous
```js
fg(patterns, [options])
```
Returns a `Promise` with an array of matching entries.
```js
const fg = require('fast-glob');
const entries = await fg(['.editorconfig', '**/index.js'], { dot: true });
// ['.editorconfig', 'services/index.js']
```
### Synchronous
```js
fg.sync(patterns, [options])
```
Returns an array of matching entries.
```js
const fg = require('fast-glob');
const entries = fg.sync(['.editorconfig', '**/index.js'], { dot: true });
// ['.editorconfig', 'services/index.js']
```
### Stream
```js
fg.stream(patterns, [options])
```
Returns a [`ReadableStream`][node_js_stream_readable_streams] when the `data` event will be emitted with matching entry.
```js
const fg = require('fast-glob');
const stream = fg.stream(['.editorconfig', '**/index.js'], { dot: true });
for await (const entry of stream) {
// .editorconfig
// services/index.js
}
```
#### patterns
* Required: `true`
* Type: `string | string[]`
Any correct pattern(s).
> :1234: [Pattern syntax](#pattern-syntax)
>
> :warning: This package does not respect the order of patterns. First, all the negative patterns are applied, and only then the positive patterns. If you want to get a certain order of records, use sorting or split calls.
#### [options]
* Required: `false`
* Type: [`Options`](#options-3)
See [Options](#options-3) section.
### Helpers
#### `generateTasks(patterns, [options])`
Returns the internal representation of patterns ([`Task`](./src/managers/tasks.ts) is a combining patterns by base directory).
```js
fg.generateTasks('*');
[{
base: '.', // Parent directory for all patterns inside this task
dynamic: true, // Dynamic or static patterns are in this task
patterns: ['*'],
positive: ['*'],
negative: []
}]
```
##### patterns
* Required: `true`
* Type: `string | string[]`
Any correct pattern(s).
##### [options]
* Required: `false`
* Type: [`Options`](#options-3)
See [Options](#options-3) section.
#### `isDynamicPattern(pattern, [options])`
Returns `true` if the passed pattern is a dynamic pattern.
> :1234: [What is a static or dynamic pattern?](#what-is-a-static-or-dynamic-pattern)
```js
fg.isDynamicPattern('*'); // true
fg.isDynamicPattern('abc'); // false
```
##### pattern
* Required: `true`
* Type: `string`
Any correct pattern.
##### [options]
* Required: `false`
* Type: [`Options`](#options-3)
See [Options](#options-3) section.
#### `escapePath(pattern)`
Returns a path with escaped special characters (`*?|(){}[]`, `!` at the beginning of line, `@+!` before the opening parenthesis).
```js
fg.escapePath('!abc'); // \\!abc
fg.escapePath('C:/Program Files (x86)'); // C:/Program Files \\(x86\\)
```
##### pattern
* Required: `true`
* Type: `string`
Any string, for example, a path to a file.
## Options
### Common options
#### concurrency
* Type: `number`
* Default: `os.cpus().length`
Specifies the maximum number of concurrent requests from a reader to read directories.
> :book: The higher the number, the higher the performance and load on the file system. If you want to read in quiet mode, set the value to a comfortable number or `1`.
#### cwd
* Type: `string`
* Default: `process.cwd()`
The current working directory in which to search.
#### deep
* Type: `number`
* Default: `Infinity`
Specifies the maximum depth of a read directory relative to the start directory.
For example, you have the following tree:
```js
dir/
└── one/ // 1
└── two/ // 2
└── file.js // 3
```
```js
// With base directory
fg.sync('dir/**', { onlyFiles: false, deep: 1 }); // ['dir/one']
fg.sync('dir/**', { onlyFiles: false, deep: 2 }); // ['dir/one', 'dir/one/two']
// With cwd option
fg.sync('**', { onlyFiles: false, cwd: 'dir', deep: 1 }); // ['one']
fg.sync('**', { onlyFiles: false, cwd: 'dir', deep: 2 }); // ['one', 'one/two']
```
> :book: If you specify a pattern with some base directory, this directory will not participate in the calculation of the depth of the found directories. Think of it as a [`cwd`](#cwd) option.
#### followSymbolicLinks
* Type: `boolean`
* Default: `true`
Indicates whether to traverse descendants of symbolic link directories when expanding `**` patterns.
> :book: Note that this option does not affect the base directory of the pattern. For example, if `./a` is a symlink to directory `./b` and you specified `['./a**', './b/**']` patterns, then directory `./a` will still be read.
> :book: If the [`stats`](#stats) option is specified, the information about the symbolic link (`fs.lstat`) will be replaced with information about the entry (`fs.stat`) behind it.
#### fs
* Type: `FileSystemAdapter`
* Default: `fs.*`
Custom implementation of methods for working with the file system.
```ts
export interface FileSystemAdapter {
lstat?: typeof fs.lstat;
stat?: typeof fs.stat;
lstatSync?: typeof fs.lstatSync;
statSync?: typeof fs.statSync;
readdir?: typeof fs.readdir;
readdirSync?: typeof fs.readdirSync;
}
```
#### ignore
* Type: `string[]`
* Default: `[]`
An array of glob patterns to exclude matches. This is an alternative way to use negative patterns.
```js
dir/
├── package-lock.json
└── package.json
```
```js
fg.sync(['*.json', '!package-lock.json']); // ['package.json']
fg.sync('*.json', { ignore: ['package-lock.json'] }); // ['package.json']
```
#### suppressErrors
* Type: `boolean`
* Default: `false`
By default this package suppress only `ENOENT` errors. Set to `true` to suppress any error.
> :book: Can be useful when the directory has entries with a special level of access.
#### throwErrorOnBrokenSymbolicLink
* Type: `boolean`
* Default: `false`
Throw an error when symbolic link is broken if `true` or safely return `lstat` call if `false`.
> :book: This option has no effect on errors when reading the symbolic link directory.
### Output control
#### absolute
* Type: `boolean`
* Default: `false`
Return the absolute path for entries.
```js
fg.sync('*.js', { absolute: false }); // ['index.js']
fg.sync('*.js', { absolute: true }); // ['/home/user/index.js']
```
> :book: This option is required if you want to use negative patterns with absolute path, for example, `!${__dirname}/*.js`.
#### markDirectories
* Type: `boolean`
* Default: `false`
Mark the directory path with the final slash.
```js
fg.sync('*', { onlyFiles: false, markDirectories: false }); // ['index.js', 'controllers']
fg.sync('*', { onlyFiles: false, markDirectories: true }); // ['index.js', 'controllers/']
```
#### objectMode
* Type: `boolean`
* Default: `false`
Returns objects (instead of strings) describing entries.
```js
fg.sync('*', { objectMode: false }); // ['src/index.js']
fg.sync('*', { objectMode: true }); // [{ name: 'index.js', path: 'src/index.js', dirent: }]
```
The object has the following fields:
* name (`string`) — the last part of the path (basename)
* path (`string`) — full path relative to the pattern base directory
* dirent ([`fs.Dirent`][node_js_fs_class_fs_dirent]) — instance of `fs.Dirent`
> :book: An object is an internal representation of entry, so getting it does not affect performance.
#### onlyDirectories
* Type: `boolean`
* Default: `false`
Return only directories.
```js
fg.sync('*', { onlyDirectories: false }); // ['index.js', 'src']
fg.sync('*', { onlyDirectories: true }); // ['src']
```
> :book: If `true`, the [`onlyFiles`](#onlyfiles) option is automatically `false`.
#### onlyFiles
* Type: `boolean`
* Default: `true`
Return only files.
```js
fg.sync('*', { onlyFiles: false }); // ['index.js', 'src']
fg.sync('*', { onlyFiles: true }); // ['index.js']
```
#### stats
* Type: `boolean`
* Default: `false`
Enables an [object mode](#objectmode) with an additional field:
* stats ([`fs.Stats`][node_js_fs_class_fs_stats]) — instance of `fs.Stats`
```js
fg.sync('*', { stats: false }); // ['src/index.js']
fg.sync('*', { stats: true }); // [{ name: 'index.js', path: 'src/index.js', dirent: , stats: }]
```
> :book: Returns `fs.stat` instead of `fs.lstat` for symbolic links when the [`followSymbolicLinks`](#followsymboliclinks) option is specified.
>
> :warning: Unlike [object mode](#objectmode) this mode requires additional calls to the file system. On average, this mode is slower at least twice. See [old and modern mode](#old-and-modern-mode) for more details.
#### unique
* Type: `boolean`
* Default: `true`
Ensures that the returned entries are unique.
```js
fg.sync(['*.json', 'package.json'], { unique: false }); // ['package.json', 'package.json']
fg.sync(['*.json', 'package.json'], { unique: true }); // ['package.json']
```
If `true` and similar entries are found, the result is the first found.
### Matching control
#### braceExpansion
* Type: `boolean`
* Default: `true`
Enables Bash-like brace expansion.
> :1234: [Syntax description][bash_hackers_syntax_expansion_brace] or more [detailed description][micromatch_braces].
```js
dir/
├── abd
├── acd
└── a{b,c}d
```
```js
fg.sync('a{b,c}d', { braceExpansion: false }); // ['a{b,c}d']
fg.sync('a{b,c}d', { braceExpansion: true }); // ['abd', 'acd']
```
#### caseSensitiveMatch
* Type: `boolean`
* Default: `true`
Enables a [case-sensitive][wikipedia_case_sensitivity] mode for matching files.
```js
dir/
├── file.txt
└── File.txt
```
```js
fg.sync('file.txt', { caseSensitiveMatch: false }); // ['file.txt', 'File.txt']
fg.sync('file.txt', { caseSensitiveMatch: true }); // ['file.txt']
```
#### dot
* Type: `boolean`
* Default: `false`
Allow patterns to match entries that begin with a period (`.`).
> :book: Note that an explicit dot in a portion of the pattern will always match dot files.
```js
dir/
├── .editorconfig
└── package.json
```
```js
fg.sync('*', { dot: false }); // ['package.json']
fg.sync('*', { dot: true }); // ['.editorconfig', 'package.json']
```
#### extglob
* Type: `boolean`
* Default: `true`
Enables Bash-like `extglob` functionality.
> :1234: [Syntax description][micromatch_extglobs].
```js
dir/
├── README.md
└── package.json
```
```js
fg.sync('*.+(json|md)', { extglob: false }); // []
fg.sync('*.+(json|md)', { extglob: true }); // ['README.md', 'package.json']
```
#### globstar
* Type: `boolean`
* Default: `true`
Enables recursively repeats a pattern containing `**`. If `false`, `**` behaves exactly like `*`.
```js
dir/
└── a
└── b
```
```js
fg.sync('**', { onlyFiles: false, globstar: false }); // ['a']
fg.sync('**', { onlyFiles: false, globstar: true }); // ['a', 'a/b']
```
#### baseNameMatch
* Type: `boolean`
* Default: `false`
If set to `true`, then patterns without slashes will be matched against the basename of the path if it contains slashes.
```js
dir/
└── one/
└── file.md
```
```js
fg.sync('*.md', { baseNameMatch: false }); // []
fg.sync('*.md', { baseNameMatch: true }); // ['one/file.md']
```
## FAQ
## What is a static or dynamic pattern?
All patterns can be divided into two types:
* **static**. A pattern is considered static if it can be used to get an entry on the file system without using matching mechanisms. For example, the `file.js` pattern is a static pattern because we can just verify that it exists on the file system.
* **dynamic**. A pattern is considered dynamic if it cannot be used directly to find occurrences without using a matching mechanisms. For example, the `*` pattern is a dynamic pattern because we cannot use this pattern directly.
A pattern is considered dynamic if it contains the following characters (`…` — any characters or their absence) or options:
* The [`caseSensitiveMatch`](#casesensitivematch) option is disabled
* `\\` (the escape character)
* `*`, `?`, `!` (at the beginning of line)
* `[…]`
* `(…|…)`
* `@(…)`, `!(…)`, `*(…)`, `?(…)`, `+(…)` (respects the [`extglob`](#extglob) option)
* `{…,…}`, `{…..…}` (respects the [`braceExpansion`](#braceexpansion) option)
## How to write patterns on Windows?
Always use forward-slashes in glob expressions (patterns and [`ignore`](#ignore) option). Use backslashes for escaping characters. With the [`cwd`](#cwd) option use a convenient format.
**Bad**
```ts
[
'directory\\*',
path.join(process.cwd(), '**')
]
```
**Good**
```ts
[
'directory/*',
path.join(process.cwd(), '**').replace(/\\/g, '/')
]
```
> :book: Use the [`normalize-path`][npm_normalize_path] or the [`unixify`][npm_unixify] package to convert Windows-style path to a Unix-style path.
Read more about [matching with backslashes][micromatch_backslashes].
## Why are parentheses match wrong?
```js
dir/
└── (special-*file).txt
```
```js
fg.sync(['(special-*file).txt']) // []
```
Refers to Bash. You need to escape special characters:
```js
fg.sync(['\\(special-*file\\).txt']) // ['(special-*file).txt']
```
Read more about [matching special characters as literals][picomatch_matching_special_characters_as_literals].
## How to exclude directory from reading?
You can use a negative pattern like this: `!**/node_modules` or `!**/node_modules/**`. Also you can use [`ignore`](#ignore) option. Just look at the example below.
```js
first/
├── file.md
└── second/
└── file.txt
```
If you don't want to read the `second` directory, you must write the following pattern: `!**/second` or `!**/second/**`.
```js
fg.sync(['**/*.md', '!**/second']); // ['first/file.md']
fg.sync(['**/*.md'], { ignore: ['**/second/**'] }); // ['first/file.md']
```
> :warning: When you write `!**/second/**/*` it means that the directory will be **read**, but all the entries will not be included in the results.
You have to understand that if you write the pattern to exclude directories, then the directory will not be read under any circumstances.
## How to use UNC path?
You cannot use [Uniform Naming Convention (UNC)][unc_path] paths as patterns (due to syntax), but you can use them as [`cwd`](#cwd) directory.
```ts
fg.sync('*', { cwd: '\\\\?\\C:\\Python27' /* or //?/C:/Python27 */ });
fg.sync('Python27/*', { cwd: '\\\\?\\C:\\' /* or //?/C:/ */ });
```
## Compatible with `node-glob`?
| node-glob | fast-glob |
| :----------: | :-------: |
| `cwd` | [`cwd`](#cwd) |
| `root` | – |
| `dot` | [`dot`](#dot) |
| `nomount` | – |
| `mark` | [`markDirectories`](#markdirectories) |
| `nosort` | – |
| `nounique` | [`unique`](#unique) |
| `nobrace` | [`braceExpansion`](#braceexpansion) |
| `noglobstar` | [`globstar`](#globstar) |
| `noext` | [`extglob`](#extglob) |
| `nocase` | [`caseSensitiveMatch`](#casesensitivematch) |
| `matchBase` | [`baseNameMatch`](#basenamematch) |
| `nodir` | [`onlyFiles`](#onlyfiles) |
| `ignore` | [`ignore`](#ignore) |
| `follow` | [`followSymbolicLinks`](#followsymboliclinks) |
| `realpath` | – |
| `absolute` | [`absolute`](#absolute) |
## Benchmarks
### Server
Link: [Vultr Bare Metal][vultr_pricing_baremetal]
* Processor: E3-1270v6 (8 CPU)
* RAM: 32GB
* Disk: SSD ([Intel DC S3520 SSDSC2BB240G7][intel_ssd])
You can see results [here][github_gist_benchmark_server] for latest release.
### Nettop
Link: [Zotac bi323][zotac_bi323]
* Processor: Intel N3150 (4 CPU)
* RAM: 8GB
* Disk: SSD ([Silicon Power SP060GBSS3S55S25][silicon_power_ssd])
You can see results [here][github_gist_benchmark_nettop] for latest release.
## Changelog
See the [Releases section of our GitHub project][github_releases] for changelog for each release version.
## License
This software is released under the terms of the MIT license.
[bash_hackers_syntax_expansion_brace]: https://wiki.bash-hackers.org/syntax/expansion/brace
[github_gist_benchmark_nettop]: https://gist.github.com/mrmlnc/f06246b197f53c356895fa35355a367c#file-fg-benchmark-nettop-product-txt
[github_gist_benchmark_server]: https://gist.github.com/mrmlnc/f06246b197f53c356895fa35355a367c#file-fg-benchmark-server-product-txt
[github_releases]: https://github.com/mrmlnc/fast-glob/releases
[glob_definition]: https://en.wikipedia.org/wiki/Glob_(programming)
[glob_linux_man]: http://man7.org/linux/man-pages/man3/glob.3.html
[intel_ssd]: https://ark.intel.com/content/www/us/en/ark/products/93012/intel-ssd-dc-s3520-series-240gb-2-5in-sata-6gb-s-3d1-mlc.html
[micromatch_backslashes]: https://github.com/micromatch/micromatch#backslashes
[micromatch_braces]: https://github.com/micromatch/braces
[micromatch_extended_globbing]: https://github.com/micromatch/micromatch#extended-globbing
[micromatch_extglobs]: https://github.com/micromatch/micromatch#extglobs
[micromatch_regex_character_classes]: https://github.com/micromatch/micromatch#regex-character-classes
[micromatch]: https://github.com/micromatch/micromatch
[node_js_fs_class_fs_dirent]: https://nodejs.org/api/fs.html#fs_class_fs_dirent
[node_js_fs_class_fs_stats]: https://nodejs.org/api/fs.html#fs_class_fs_stats
[node_js_stream_readable_streams]: https://nodejs.org/api/stream.html#stream_readable_streams
[node_js]: https://nodejs.org/en
[nodelib_fs_scandir_old_and_modern_modern]: https://github.com/nodelib/nodelib/blob/master/packages/fs/fs.scandir/README.md#old-and-modern-mode
[npm_normalize_path]: https://www.npmjs.com/package/normalize-path
[npm_unixify]: https://www.npmjs.com/package/unixify
[paypal_mrmlnc]:https://paypal.me/mrmlnc
[picomatch_matching_behavior]: https://github.com/micromatch/picomatch#matching-behavior-vs-bash
[picomatch_matching_special_characters_as_literals]: https://github.com/micromatch/picomatch#matching-special-characters-as-literals
[picomatch_posix_brackets]: https://github.com/micromatch/picomatch#posix-brackets
[regular_expressions_brackets]: https://www.regular-expressions.info/brackets.html
[silicon_power_ssd]: https://www.silicon-power.com/web/product-1
[unc_path]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/62e862f4-2a51-452e-8eeb-dc4ff5ee33cc
[vultr_pricing_baremetal]: https://www.vultr.com/pricing/baremetal
[wikipedia_case_sensitivity]: https://en.wikipedia.org/wiki/Case_sensitivity
[zotac_bi323]: https://www.zotac.com/ee/product/mini_pcs/zbox-bi323
fast-glob-3.2.12/fixtures/ 0000775 0000000 0000000 00000000000 14306550000 0015317 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/.directory/ 0000775 0000000 0000000 00000000000 14306550000 0017401 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/.directory/file.md 0000664 0000000 0000000 00000000000 14306550000 0020630 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/.file 0000664 0000000 0000000 00000000000 14306550000 0016225 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/file.md 0000664 0000000 0000000 00000000000 14306550000 0016546 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/ 0000775 0000000 0000000 00000000000 14306550000 0016446 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/file.md 0000664 0000000 0000000 00000000000 14306550000 0017675 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/nested/ 0000775 0000000 0000000 00000000000 14306550000 0017730 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/nested/directory/ 0000775 0000000 0000000 00000000000 14306550000 0021734 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/nested/directory/file.json 0000664 0000000 0000000 00000000000 14306550000 0023534 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/nested/directory/file.md 0000664 0000000 0000000 00000000000 14306550000 0023163 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/first/nested/file.md 0000664 0000000 0000000 00000000000 14306550000 0021157 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/ 0000775 0000000 0000000 00000000000 14306550000 0016572 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/file.md 0000664 0000000 0000000 00000000000 14306550000 0020021 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/nested/ 0000775 0000000 0000000 00000000000 14306550000 0020054 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/nested/directory/ 0000775 0000000 0000000 00000000000 14306550000 0022060 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/nested/directory/file.md 0000664 0000000 0000000 00000000000 14306550000 0023307 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/second/nested/file.md 0000664 0000000 0000000 00000000000 14306550000 0021303 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/ 0000775 0000000 0000000 00000000000 14306550000 0016431 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/library/ 0000775 0000000 0000000 00000000000 14306550000 0020075 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/library/a/ 0000775 0000000 0000000 00000000000 14306550000 0020315 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/library/a/book.md 0000664 0000000 0000000 00000000000 14306550000 0021557 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/library/b/ 0000775 0000000 0000000 00000000000 14306550000 0020316 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/fixtures/third/library/b/book.md 0000664 0000000 0000000 00000000000 14306550000 0021560 0 ustar 00root root 0000000 0000000 fast-glob-3.2.12/package.json 0000664 0000000 0000000 00000007423 14306550000 0015742 0 ustar 00root root 0000000 0000000 {
"name": "fast-glob",
"version": "3.2.12",
"description": "It's a very fast and efficient glob library for Node.js",
"license": "MIT",
"repository": "mrmlnc/fast-glob",
"author": {
"name": "Denis Malinochkin",
"url": "https://mrmlnc.com"
},
"engines": {
"node": ">=8.6.0"
},
"main": "out/index.js",
"typings": "out/index.d.ts",
"files": [
"out",
"!out/{benchmark,tests}",
"!out/**/*.map",
"!out/**/*.spec.*"
],
"keywords": [
"glob",
"patterns",
"fast",
"implementation"
],
"devDependencies": {
"@nodelib/fs.macchiato": "^1.0.1",
"@types/compute-stdev": "^1.0.0",
"@types/easy-table": "^0.0.32",
"@types/glob": "^7.1.1",
"@types/glob-parent": "^5.1.0",
"@types/is-ci": "^2.0.0",
"@types/merge2": "^1.1.4",
"@types/micromatch": "^4.0.0",
"@types/minimist": "^1.2.0",
"@types/mocha": "^5.2.7",
"@types/node": "^12.7.8",
"@types/rimraf": "^2.0.2",
"@types/sinon": "^7.5.0",
"compute-stdev": "^1.0.0",
"easy-table": "^1.1.1",
"eslint": "^6.5.1",
"eslint-config-mrmlnc": "^1.1.0",
"execa": "^2.0.4",
"fast-glob": "^3.0.4",
"fdir": "^5.1.0",
"glob": "^7.1.4",
"is-ci": "^2.0.0",
"log-update": "^4.0.0",
"minimist": "^1.2.0",
"mocha": "^6.2.1",
"rimraf": "^3.0.0",
"sinon": "^7.5.0",
"tiny-glob": "^0.2.6",
"typescript": "^3.6.3"
},
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"scripts": {
"clean": "rimraf out",
"lint": "eslint \"src/**/*.ts\" --cache",
"compile": "tsc",
"test": "mocha \"out/**/*.spec.js\" -s 0",
"smoke": "mocha \"out/**/*.smoke.js\" -s 0",
"smoke:sync": "mocha \"out/**/*.smoke.js\" -s 0 --grep \"\\(sync\\)\"",
"smoke:async": "mocha \"out/**/*.smoke.js\" -s 0 --grep \"\\(async\\)\"",
"smoke:stream": "mocha \"out/**/*.smoke.js\" -s 0 --grep \"\\(stream\\)\"",
"build": "npm run clean && npm run compile && npm run lint && npm test",
"watch": "npm run clean && npm run compile -- --sourceMap --watch",
"bench": "npm run bench-async && npm run bench-stream && npm run bench-sync",
"bench-async": "npm run bench-async-flatten && npm run bench-async-deep && npm run bench-async-partial-flatten && npm run bench-async-partial-deep",
"bench-stream": "npm run bench-stream-flatten && npm run bench-stream-deep && npm run bench-stream-partial-flatten && npm run bench-stream-partial-deep",
"bench-sync": "npm run bench-sync-flatten && npm run bench-sync-deep && npm run bench-sync-partial-flatten && npm run bench-sync-partial-deep",
"bench-async-flatten": "node ./out/benchmark --mode async --pattern \"*\"",
"bench-async-deep": "node ./out/benchmark --mode async --pattern \"**\"",
"bench-async-partial-flatten": "node ./out/benchmark --mode async --pattern \"{fixtures,out}/{first,second}/*\"",
"bench-async-partial-deep": "node ./out/benchmark --mode async --pattern \"{fixtures,out}/**\"",
"bench-stream-flatten": "node ./out/benchmark --mode stream --pattern \"*\"",
"bench-stream-deep": "node ./out/benchmark --mode stream --pattern \"**\"",
"bench-stream-partial-flatten": "node ./out/benchmark --mode stream --pattern \"{fixtures,out}/{first,second}/*\"",
"bench-stream-partial-deep": "node ./out/benchmark --mode stream --pattern \"{fixtures,out}/**\"",
"bench-sync-flatten": "node ./out/benchmark --mode sync --pattern \"*\"",
"bench-sync-deep": "node ./out/benchmark --mode sync --pattern \"**\"",
"bench-sync-partial-flatten": "node ./out/benchmark --mode sync --pattern \"{fixtures,out}/{first,second}/*\"",
"bench-sync-partial-deep": "node ./out/benchmark --mode sync --pattern \"{fixtures,out}/**\""
}
}
fast-glob-3.2.12/src/ 0000775 0000000 0000000 00000000000 14306550000 0014235 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/ 0000775 0000000 0000000 00000000000 14306550000 0016167 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/index.ts 0000664 0000000 0000000 00000002544 14306550000 0017653 0 ustar 00root root 0000000 0000000 import * as minimist from 'minimist';
import * as logger from './logger';
import Runner, { RunnerOptions } from './runner';
import * as utils from './utils';
const PROCESS_FIRST_ARGUMENT_INDEX = 2;
const DEFAULT_BENCHMARK_LAUNCHES = 10;
const DEFAULT_BENCHMARK_MAX_STDEV = 3;
const DEFAULT_BENCHMARK_RETRIES = 5;
type Arguments = RunnerOptions & {
basedir: string;
};
const defaultArgv: Arguments = {
basedir: '.',
type: utils.getEnvironmentAsString('BENCHMARK_TYPE', 'product'),
mode: utils.getEnvironmentAsString('BENCHMARK_MODE', 'async'),
pattern: utils.getEnvironmentAsString('BENCHMARK_PATTERN', '*'),
launches: utils.getEnvironmentAsInteger('BENCHMARK_LAUNCHES', DEFAULT_BENCHMARK_LAUNCHES),
maxStdev: utils.getEnvironmentAsInteger('BENCHMARK_MAX_STDEV', DEFAULT_BENCHMARK_MAX_STDEV),
retries: utils.getEnvironmentAsInteger('BENCHMARK_RETRIES', DEFAULT_BENCHMARK_RETRIES),
options: utils.getEnvironmentAsObject('BENCHMARK_OPTIONS', {})
};
const argv = minimist(process.argv.slice(PROCESS_FIRST_ARGUMENT_INDEX), {
default: defaultArgv
});
const runner = new Runner(argv.basedir, argv);
logger.head(`Benchmark pattern "${argv.pattern}" with ${argv.launches} launches (${argv.type}, ${argv.mode})`);
logger.head(`Max stdev: ${argv.maxStdev} | Retries: ${argv.retries} | Options: ${JSON.stringify(argv.options)}`);
logger.newline();
runner.packs();
fast-glob-3.2.12/src/benchmark/logger.ts 0000664 0000000 0000000 00000000207 14306550000 0020015 0 ustar 00root root 0000000 0000000 export function head(message: string): void {
console.info('===> ' + message);
}
export function newline(): void {
console.log();
}
fast-glob-3.2.12/src/benchmark/reporter.spec.ts 0000664 0000000 0000000 00000001665 14306550000 0021342 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import Reporter from './reporter';
import { SuitePackResult } from './runner';
describe('Benchmark → Reporter', () => {
const result: SuitePackResult = {
name: 'name',
errors: 0,
retries: 1,
entries: 1,
measures: {
time: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'ms' },
memory: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'MB' }
}
};
describe('.format', () => {
it('should returns report', () => {
const reporter = new Reporter();
reporter.row(result);
const expected = [
'Name Time, ms Time stdev, % Memory, MB Memory stdev, % Entries Errors Retries',
'---- -------- ------------- ---------- --------------- ------- ------ -------',
'name 1.000 0.000 1.000 0.000 1 0 1 ',
''
].join('\n');
const actual = reporter.format();
assert.strictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/benchmark/reporter.ts 0000664 0000000 0000000 00000003132 14306550000 0020400 0 ustar 00root root 0000000 0000000 import * as logUpdate from 'log-update';
import * as isCi from 'is-ci';
import { SuitePackResult, Measure } from './runner';
import Table = require('easy-table'); // eslint-disable-line @typescript-eslint/no-require-imports
const FRACTION_DIGITS = 3;
export default class Reporter {
private readonly _table: Table = new Table();
private readonly _log: logUpdate.LogUpdate = logUpdate.create(process.stdout);
public row(result: SuitePackResult): void {
this._table.cell('Name', result.name);
this._table.cell(`Time, ${result.measures.time.units}`, this._formatMeasureValue(result.measures.time));
this._table.cell('Time stdev, %', this._formatMeasureStdevValue(result.measures.time));
this._table.cell(`Memory, ${result.measures.memory.units}`, this._formatMeasureValue(result.measures.memory));
this._table.cell('Memory stdev, %', this._formatMeasureStdevValue(result.measures.memory));
this._table.cell('Entries', result.entries);
this._table.cell('Errors', result.errors);
this._table.cell('Retries', result.retries);
this._table.newRow();
}
public format(): string {
return this._table.toString();
}
public display(): void {
if (!isCi) {
this._log(this.format());
}
}
public reset(): void {
if (isCi) {
console.log(this.format());
}
this._log.done();
}
private _formatMeasureValue(measure: Measure): string {
return this._formatMeasure(measure.average);
}
private _formatMeasureStdevValue(measure: Measure): string {
return this._formatMeasure(measure.stdev);
}
private _formatMeasure(value: number): string {
return value.toFixed(FRACTION_DIGITS);
}
}
fast-glob-3.2.12/src/benchmark/runner.spec.ts 0000664 0000000 0000000 00000005457 14306550000 0021014 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import Runner, { RunnerOptions, SuiteMeasures, SuitePackResult } from './runner';
import Reporter from './reporter';
class RunnerFakeProcess extends Runner {
public execNodeProcess(): string {
return '{"matches":1,"time":1,"memory":1}';
}
}
class RunnerFakeProcessError extends Runner {
public execNodeProcess(): string {
return 'error';
}
}
class RunnerFakeReport extends RunnerFakeProcess {
public results: SuitePackResult[] = [];
public report(_: Reporter, result: SuitePackResult): void {
this.results.push(result);
}
public getSuites(): string[] {
return ['suite.js'];
}
}
describe('Benchmark → Runner', () => {
const runnerOptions: RunnerOptions = {
type: 'product',
mode: 'async',
pattern: '*',
launches: 3,
maxStdev: 3,
retries: 5,
options: {}
};
describe('.suite', () => {
it('should returns measures', () => {
const runner = new RunnerFakeProcess('basedir', runnerOptions);
const expected: SuiteMeasures = {
matches: 1,
time: 1,
memory: 1
};
const actual = runner.suite('suitePath');
assert.deepStrictEqual(actual, expected);
});
it('should throw error', () => {
const runner = new RunnerFakeProcessError('basedir', runnerOptions);
assert.throws(() => runner.suite('suitePath'), /Ops! Broken suite run\./);
});
});
describe('.suitePack', () => {
it('should returns pack measures', () => {
const runner = new RunnerFakeProcess('basedir', runnerOptions);
const expected: SuitePackResult = {
name: 'suitePath',
errors: 0,
retries: 1,
entries: 1,
measures: {
time: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'ms' },
memory: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'MB' }
}
};
const actual = runner.suitePack('suitePath', 0);
assert.deepStrictEqual(actual, expected);
});
it('should returns pack measures with errors', () => {
const runner = new RunnerFakeProcessError('basedir', runnerOptions);
const expected: SuitePackResult = {
name: 'suitePath',
errors: 3,
retries: 1,
entries: 0,
measures: {
time: { raw: [0, 0, 0], average: 0, stdev: 0, units: 'ms' },
memory: { raw: [0, 0, 0], average: 0, stdev: 0, units: 'MB' }
}
};
const actual = runner.suitePack('suitePath', 0);
assert.deepStrictEqual(actual, expected);
});
});
describe('.packs', () => {
it('should run pack of suites', () => {
const runner = new RunnerFakeReport('basedir', runnerOptions);
const expected = [{
name: 'suite.js',
errors: 0,
entries: 1,
retries: 1,
measures: {
time: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'ms' },
memory: { raw: [1, 1, 1], average: 1, stdev: 0, units: 'MB' }
}
}];
runner.packs();
assert.deepStrictEqual(runner.results, expected);
});
});
});
fast-glob-3.2.12/src/benchmark/runner.ts 0000664 0000000 0000000 00000007073 14306550000 0020057 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import * as path from 'path';
import { Options } from '../settings';
import Reporter from './reporter';
import * as utils from './utils';
import execa = require('execa'); // eslint-disable-line @typescript-eslint/no-require-imports
export type RunnerOptions = {
type: string;
mode: string;
pattern: string;
launches: number;
maxStdev: number;
retries: number;
options: Options;
};
export type SuiteMeasures = {
matches: number;
time: number;
memory: number;
};
export type Measure = {
units: string;
raw: number[];
average: number;
stdev: number;
};
export type SuitePackMeasures = {
time: Measure;
memory: Measure;
};
export type SuitePackResult = {
name: string;
errors: number;
entries: number;
retries: number;
measures: SuitePackMeasures;
};
export default class Runner {
constructor(private readonly _basedir: string, private readonly _options: RunnerOptions) { }
public execNodeProcess(args: string[], options: Partial): string {
return execa.sync('node', args, options).stdout;
}
/**
* Runs a single suite in the child process and returns the measurements of his work.
*/
public suite(suitePath: string): SuiteMeasures {
const environment: NodeJS.ProcessEnv = {
NODE_ENV: 'production',
BENCHMARK_BASE_DIR: this._basedir,
BENCHMARK_PATTERN: this._options.pattern,
BENCHMARK_OPTIONS: JSON.stringify(this._options.options)
};
const execaOptions: execa.SyncOptions = { env: environment, extendEnv: true };
const stdout = this.execNodeProcess([suitePath], execaOptions);
try {
return JSON.parse(stdout) as SuiteMeasures;
} catch {
throw new TypeError('Ops! Broken suite run.');
}
}
public suitePack(suitePath: string, retries: number): SuitePackResult {
const results: SuitePackResult = {
name: path.basename(suitePath),
errors: 0,
entries: 0,
retries: retries + 1,
measures: this._getSuitePackMeasures()
};
for (let i = 0; i < this._options.launches; i++) {
try {
const { matches, time, memory } = this.suite(suitePath);
results.entries = matches;
results.measures.time.raw.push(time);
results.measures.memory.raw.push(memory);
} catch {
results.errors++;
results.measures.time.raw.push(0);
results.measures.memory.raw.push(0);
}
}
results.measures = {
time: this._getMeasures(results.measures.time.raw, 'ms'),
memory: this._getMeasures(results.measures.memory.raw, 'MB')
};
return results;
}
public report(reporter: Reporter, result: SuitePackResult): void {
reporter.row(result);
reporter.display();
}
public packs(): void {
const suitesPath = path.join(__dirname, 'suites', this._options.type, this._options.mode);
const suites = this.getSuites(suitesPath);
const reporter = new Reporter();
for (const filepath of suites) {
const suitePath = path.join(suitesPath, filepath);
let result = this.suitePack(suitePath, 0);
while (result.measures.time.stdev > this._options.maxStdev && result.retries < this._options.retries) {
result = this.suitePack(suitePath, result.retries);
}
this.report(reporter, result);
}
reporter.reset();
}
public getSuites(suitesPath: string): string[] {
return fs.readdirSync(suitesPath).filter((suite) => suite.endsWith('.js'));
}
private _getMeasures(raw: number[], units: string): Measure {
return {
units,
raw,
average: utils.getAverageValue(raw),
stdev: utils.getStdev(raw)
};
}
private _getSuitePackMeasures(): SuitePackMeasures {
return {
time: this._getMeasures([], 'ms'),
memory: this._getMeasures([], 'MB')
};
}
}
fast-glob-3.2.12/src/benchmark/suites/ 0000775 0000000 0000000 00000000000 14306550000 0017503 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/product/ 0000775 0000000 0000000 00000000000 14306550000 0021163 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/product/async/ 0000775 0000000 0000000 00000000000 14306550000 0022300 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/product/async/fast-glob.ts 0000664 0000000 0000000 00000001071 14306550000 0024525 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false
};
const timeStart = utils.timeStart();
glob(process.env.BENCHMARK_PATTERN as string, options)
.then((matches) => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
})
.catch(() => {
process.exit(0);
});
fast-glob-3.2.12/src/benchmark/suites/product/async/fdir.ts 0000664 0000000 0000000 00000001206 14306550000 0023573 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import { fdir as GlobBuilder, PathsOutput } from 'fdir';
import * as utils from '../../../utils';
const CWD = path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string);
const PATTERN = process.env.BENCHMARK_PATTERN as string;
const fdir = new GlobBuilder()
.glob(PATTERN)
.crawl(CWD);
const timeStart = utils.timeStart();
fdir.withPromise()
.then((matches) => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures((matches as PathsOutput).length, time, memory);
console.info(measures);
})
.catch(() => {
process.exit(0);
});
fast-glob-3.2.12/src/benchmark/suites/product/async/node-glob.ts 0000664 0000000 0000000 00000001124 14306550000 0024514 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from 'glob';
import * as utils from '../../../utils';
const options: glob.IOptions = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
nosort: true,
nounique: true,
nodir: true
};
const timeStart = utils.timeStart();
glob(process.env.BENCHMARK_PATTERN as string, options, (error, matches) => {
if (error !== null) {
process.exit(0);
}
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
});
fast-glob-3.2.12/src/benchmark/suites/product/async/tiny-glob.ts 0000664 0000000 0000000 00000001146 14306550000 0024556 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as utils from '../../../utils';
import glob = require('tiny-glob'); // eslint-disable-line @typescript-eslint/no-require-imports
const options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
flush: true
};
const timeStart = utils.timeStart();
glob(process.env.BENCHMARK_PATTERN as string, options)
.then((matches) => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
})
.catch(() => {
process.exit(0);
});
fast-glob-3.2.12/src/benchmark/suites/product/stream/ 0000775 0000000 0000000 00000000000 14306550000 0022456 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/product/stream/fast-glob.ts 0000664 0000000 0000000 00000001304 14306550000 0024702 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
objectMode: true
};
const entries: string[] = [];
const timeStart = utils.timeStart();
const stream = glob.stream(process.env.BENCHMARK_PATTERN as string, options);
stream.once('error', () => process.exit(0));
stream.on('data', (entry: string) => entries.push(entry));
stream.once('end', () => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(entries.length, time, memory);
console.info(measures);
});
fast-glob-3.2.12/src/benchmark/suites/product/sync/ 0000775 0000000 0000000 00000000000 14306550000 0022137 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/product/sync/fast-glob.ts 0000664 0000000 0000000 00000001056 14306550000 0024367 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false
};
const timeStart = utils.timeStart();
try {
const matches = glob.sync(process.env.BENCHMARK_PATTERN as string, options);
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/suites/product/sync/fdir.ts 0000664 0000000 0000000 00000001155 14306550000 0023435 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import { fdir as GlobBuilder, PathsOutput } from 'fdir';
import * as utils from '../../../utils';
const CWD = path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string);
const PATTERN = process.env.BENCHMARK_PATTERN as string;
const glob = new GlobBuilder()
.glob(PATTERN)
.crawl(CWD);
const timeStart = utils.timeStart();
try {
const matches = glob.sync() as PathsOutput;
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/suites/product/sync/node-glob.ts 0000664 0000000 0000000 00000001107 14306550000 0024354 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from 'glob';
import * as utils from '../../../utils';
const options: glob.IOptions = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
nosort: true,
nounique: true,
nodir: true
};
const timeStart = utils.timeStart();
try {
const matches = glob.sync(process.env.BENCHMARK_PATTERN as string, options);
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/suites/product/sync/tiny-glob.ts 0000664 0000000 0000000 00000001140 14306550000 0024407 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as utils from '../../../utils';
// eslint-disable-next-line @typescript-eslint/no-require-imports
import glob = require('tiny-glob/sync');
const options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
flush: true
};
const timeStart = utils.timeStart();
try {
const matches = glob(process.env.BENCHMARK_PATTERN as string, options);
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/suites/regression/ 0000775 0000000 0000000 00000000000 14306550000 0021663 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/regression/async/ 0000775 0000000 0000000 00000000000 14306550000 0023000 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/regression/async/fast-glob-current.ts 0000664 0000000 0000000 00000001162 14306550000 0026706 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const timeStart = utils.timeStart();
glob(process.env.BENCHMARK_PATTERN as string, options)
.then((matches) => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
})
.catch(() => {
process.exit(0);
});
fast-glob-3.2.12/src/benchmark/suites/regression/async/fast-glob-previous.ts 0000664 0000000 0000000 00000001153 14306550000 0027100 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as fg from 'fast-glob';
import * as utils from '../../../utils';
const options: fg.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const timeStart = utils.timeStart();
fg(process.env.BENCHMARK_PATTERN as string, options)
.then((matches) => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
})
.catch(() => {
process.exit(0);
});
fast-glob-3.2.12/src/benchmark/suites/regression/stream/ 0000775 0000000 0000000 00000000000 14306550000 0023156 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/regression/stream/fast-glob-current.ts 0000664 0000000 0000000 00000001352 14306550000 0027065 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const entries: string[] = [];
const timeStart = utils.timeStart();
const stream = glob.stream(process.env.BENCHMARK_PATTERN as string, options);
stream.once('error', () => process.exit(0));
stream.on('data', (entry: string) => entries.push(entry));
stream.once('end', () => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(entries.length, time, memory);
console.info(measures);
});
fast-glob-3.2.12/src/benchmark/suites/regression/stream/fast-glob-previous.ts 0000664 0000000 0000000 00000001343 14306550000 0027257 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as fg from 'fast-glob';
import * as utils from '../../../utils';
const options: fg.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const entries: string[] = [];
const timeStart = utils.timeStart();
const stream = fg.stream(process.env.BENCHMARK_PATTERN as string, options);
stream.once('error', () => process.exit(0));
stream.on('data', (entry: string) => entries.push(entry));
stream.once('end', () => {
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(entries.length, time, memory);
console.info(measures);
});
fast-glob-3.2.12/src/benchmark/suites/regression/sync/ 0000775 0000000 0000000 00000000000 14306550000 0022637 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/benchmark/suites/regression/sync/fast-glob-current.ts 0000664 0000000 0000000 00000001147 14306550000 0026550 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as glob from '../../../..';
import * as utils from '../../../utils';
const options: glob.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const timeStart = utils.timeStart();
try {
const matches = glob.sync(process.env.BENCHMARK_PATTERN as string, options);
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/suites/regression/sync/fast-glob-previous.ts 0000664 0000000 0000000 00000001140 14306550000 0026733 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as fg from 'fast-glob';
import * as utils from '../../../utils';
const options: fg.Options = {
cwd: path.join(process.cwd(), process.env.BENCHMARK_BASE_DIR as string),
unique: false,
...JSON.parse(process.env.BENCHMARK_OPTIONS as string)
};
const timeStart = utils.timeStart();
try {
const matches = fg.sync(process.env.BENCHMARK_PATTERN as string, options);
const memory = utils.getMemory();
const time = utils.timeEnd(timeStart);
const measures = utils.formatMeasures(matches.length, time, memory);
console.info(measures);
} catch {
process.exit(0);
}
fast-glob-3.2.12/src/benchmark/utils.spec.ts 0000664 0000000 0000000 00000007261 14306550000 0020636 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as utils from './utils';
describe('Benchmark → Utils', () => {
const oldProcessHrtime = process.hrtime;
const oldProcessMemoryUsage = process.memoryUsage;
before(() => {
process.env.FG_TEST_ENV_INTEGER = '1';
process.env.FG_TEST_ENV_OBJECT = '{ "value": true }';
process.hrtime = (() => [0, 1e7]) as NodeJS.HRTime;
process.memoryUsage = () => ({ external: 0, rss: 0, heapTotal: 0, heapUsed: 10 * 1e6 });
});
after(() => {
delete process.env.FG_TEST_ENV_INTEGER;
delete process.env.FG_TEST_ENV_OBJECT;
process.hrtime = oldProcessHrtime;
process.memoryUsage = oldProcessMemoryUsage;
});
describe('.convertHrtimeToMilliseconds', () => {
it('should return milliseconds', () => {
const hrtime: [number, number] = [0, 1e7];
const expected = 10;
const actual = utils.convertHrtimeToMilliseconds(hrtime);
assert.strictEqual(actual, expected);
});
});
describe('.convertBytesToMegaBytes', () => {
it('should return megabytes', () => {
const expected = 1;
const actual = utils.convertBytesToMegaBytes(1e6);
assert.strictEqual(actual, expected);
});
});
describe('.timeStart', () => {
it('should return hrtime', () => {
const expected: [number, number] = [0, 1e7];
const actual = utils.timeStart();
assert.deepStrictEqual(actual, expected);
});
});
describe('.timeEnd', () => {
it('should return diff between hrtime\'s', () => {
const expected = 10;
const actual = utils.timeEnd([0, 1e7]);
assert.strictEqual(actual, expected);
});
});
describe('.getMemory', () => {
it('should return memory usage in megabytes', () => {
const expected = 10;
const actual = utils.getMemory();
assert.strictEqual(actual, expected);
});
});
describe('.getMeasures', () => {
it('should return measures', () => {
const expected = '{"matches":1,"time":1,"memory":1}';
const actual = utils.formatMeasures(1, 1, 1);
assert.strictEqual(actual, expected);
});
});
describe('.getAverageValue', () => {
it('should return average value for array', () => {
const expected = 2;
const actual = utils.getAverageValue([3, 1, 2]);
assert.strictEqual(actual, expected);
});
});
describe('.getStdev', () => {
it('should return stdev for array', () => {
const expected = 1;
const actual = utils.getStdev([1, 2, 3]);
assert.strictEqual(actual, expected);
});
});
describe('.getEnvironmentAsString', () => {
it('should return string', () => {
const expected = 'text';
const actual = utils.getEnvironmentAsString('FG_TEST_ENV_STRING', 'text');
assert.strictEqual(actual, expected);
});
it('should return default value', () => {
const expected = '';
const actual = utils.getEnvironmentAsString('NON_EXIST_ENV_VARIABLE', '');
assert.strictEqual(actual, expected);
});
});
describe('.getEnvironmentAsInteger', () => {
it('should return integer', () => {
const expected = 1;
const actual = utils.getEnvironmentAsInteger('FG_TEST_ENV_INTEGER', 0);
assert.strictEqual(actual, expected);
});
it('should return default value', () => {
const expected = 0;
const actual = utils.getEnvironmentAsInteger('NON_EXIST_ENV_VARIABLE', 0);
assert.strictEqual(actual, expected);
});
});
describe('.getEnvironmentAsObject', () => {
it('should return object', () => {
const expected = { value: true };
const actual = utils.getEnvironmentAsObject('FG_TEST_ENV_OBJECT', {});
assert.deepStrictEqual(actual, expected);
});
it('should return default value', () => {
const expected = {};
const actual = utils.getEnvironmentAsObject('NON_EXIST_ENV_VARIABLE', {});
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/benchmark/utils.ts 0000664 0000000 0000000 00000003462 14306550000 0017704 0 ustar 00root root 0000000 0000000 import { SuiteMeasures } from './runner';
import stdev = require('compute-stdev'); // eslint-disable-line @typescript-eslint/no-require-imports
const NANOSECONDS_IN_SECOND = 1e9;
const MICROSECONDS_IN_SECOND = 1e6;
const BYTES_IN_MEGABYTE = 1e6;
export function convertHrtimeToMilliseconds(hrtime: [number, number]): number {
const nanoseconds = hrtime[0] * NANOSECONDS_IN_SECOND;
return (nanoseconds + hrtime[1]) / MICROSECONDS_IN_SECOND;
}
export function convertBytesToMegaBytes(bytes: number): number {
return bytes / MICROSECONDS_IN_SECOND;
}
export function timeStart(): [number, number] {
return process.hrtime();
}
export function timeEnd(start: [number, number]): number {
const hrtime = process.hrtime(start);
return convertHrtimeToMilliseconds(hrtime);
}
export function getMemory(): number {
return process.memoryUsage().heapUsed / BYTES_IN_MEGABYTE;
}
export function formatMeasures(matches: number, time: number, memory: number): string {
const measures: SuiteMeasures = { matches, time, memory };
return JSON.stringify(measures);
}
export function getAverageValue(values: number[]): number {
return values.reduce((a, b) => a + b, 0) / values.length;
}
export function getStdev(values: number[]): number {
return stdev(values);
}
export function getEnvironmentAsString(name: string, value: string): string {
const environment = process.env[name];
return environment === undefined ? value : environment;
}
export function getEnvironmentAsInteger(name: string, value: number): number {
const environment = process.env[name];
return environment === undefined ? value : parseInt(environment, 10);
}
export function getEnvironmentAsObject(name: string, value: object): object {
const environment = process.env[name];
return environment === undefined ? value : JSON.parse(environment) as object;
}
fast-glob-3.2.12/src/index.spec.ts 0000664 0000000 0000000 00000015061 14306550000 0016650 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as tests from './tests';
import { EntryItem, ErrnoException } from './types';
import * as fg from '.';
describe('Package', () => {
describe('.sync', () => {
it('should throw an error when input values can not pass validation', () => {
const message = 'Patterns must be a string (non empty) or an array of strings';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.throws(() => fg.sync(null as any), { message });
assert.throws(() => fg.sync(''), { message });
});
it('should returns entries', () => {
const expected: EntryItem[] = [
'fixtures/file.md',
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md',
'fixtures/third/library/a/book.md',
'fixtures/third/library/b/book.md'
];
const actual = fg.sync(['fixtures/**/*.md']);
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
});
it('should returns entries (two sources)', () => {
const expected: EntryItem[] = [
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md'
];
const actual = fg.sync(['fixtures/first/**/*.md', 'fixtures/second/**/*.md']);
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
});
});
describe('.async', () => {
it('should throw an error when input values can not pass validation', async () => {
const message = 'Patterns must be a string (non empty) or an array of strings';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await assert.rejects(() => fg(null as any), { message });
await assert.rejects(() => fg(''), { message });
});
it('should returns entries', async () => {
const expected: EntryItem[] = [
'fixtures/file.md',
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md',
'fixtures/third/library/a/book.md',
'fixtures/third/library/b/book.md'
];
const actual = await fg(['fixtures/**/*.md']);
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
});
it('should returns entries (two sources)', async () => {
const expected: EntryItem[] = [
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md'
];
const actual = await fg(['fixtures/first/**/*.md', 'fixtures/second/**/*.md']);
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
});
});
describe('.stream', () => {
it('should throw an error when input values can not pass validation', () => {
const message = 'Patterns must be a string (non empty) or an array of strings';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.throws(() => fg.stream(null as any), { message });
assert.throws(() => fg.stream(''), { message });
});
it('should returns entries', (done) => {
const expected: string[] = [
'fixtures/file.md',
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md',
'fixtures/third/library/a/book.md',
'fixtures/third/library/b/book.md'
];
const actual: string[] = [];
const stream = fg.stream(['fixtures/**/*.md']);
stream.on('data', (entry: string) => actual.push(entry));
stream.once('error', (error: ErrnoException) => assert.fail(error));
stream.once('end', () => {
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
done();
});
});
it('should returns entries (two sources)', (done) => {
const expected: EntryItem[] = [
'fixtures/first/file.md',
'fixtures/first/nested/directory/file.md',
'fixtures/first/nested/file.md',
'fixtures/second/file.md',
'fixtures/second/nested/directory/file.md',
'fixtures/second/nested/file.md'
];
const actual: string[] = [];
const stream = fg.stream(['fixtures/first/**/*.md', 'fixtures/second/**/*.md']);
stream.on('data', (entry: string) => actual.push(entry));
stream.once('error', (error: ErrnoException) => assert.fail(error));
stream.once('end', () => {
actual.sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(actual, expected);
done();
});
});
});
describe('.generateTasks', () => {
it('should throw an error when input values can not pass validation', () => {
const message = 'Patterns must be a string (non empty) or an array of strings';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.throws(() => fg.generateTasks(null as any), { message });
assert.throws(() => fg.generateTasks(''), { message });
});
it('should return tasks', () => {
const expected = [
tests.task.builder().base('.').positive('*').build()
];
const actual = fg.generateTasks(['*']);
assert.deepStrictEqual(actual, expected);
});
it('should return tasks with negative patterns', () => {
const expected = [
tests.task.builder().base('.').positive('*').negative('*.txt').negative('*.md').build()
];
const actual = fg.generateTasks(['*', '!*.txt'], { ignore: ['*.md'] });
assert.deepStrictEqual(actual, expected);
});
it('should clean up patterns', () => {
const expected = [
// Clean up duplicate slashes
tests.task.builder().base('fixtures').positive('fixtures/*').build()
];
const actual = fg.generateTasks(['fixtures//*']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.isDynamicPattern', () => {
it('should return true for dynamic pattern', () => {
assert.ok(fg.isDynamicPattern('*'));
});
it('should return false for static pattern', () => {
assert.ok(!fg.isDynamicPattern('abc'));
});
});
describe('.escapePath', () => {
it('should return escaped path', () => {
const expected = 'C:/Program Files \\(x86\\)';
const actual = fg.escapePath('C:/Program Files (x86)');
assert.strictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/index.ts 0000664 0000000 0000000 00000010117 14306550000 0015714 0 ustar 00root root 0000000 0000000 import * as taskManager from './managers/tasks';
import * as patternManager from './managers/patterns';
import ProviderAsync from './providers/async';
import Provider from './providers/provider';
import ProviderStream from './providers/stream';
import ProviderSync from './providers/sync';
import Settings, { Options as OptionsInternal } from './settings';
import { Entry as EntryInternal, EntryItem, FileSystemAdapter as FileSystemAdapterInternal, Pattern as PatternInternal } from './types';
import * as utils from './utils';
type EntryObjectModePredicate = { [TKey in keyof Pick]-?: true };
type EntryStatsPredicate = { [TKey in keyof Pick]-?: true };
type EntryObjectPredicate = EntryObjectModePredicate | EntryStatsPredicate;
function FastGlob(source: PatternInternal | PatternInternal[], options: OptionsInternal & EntryObjectPredicate): Promise;
function FastGlob(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Promise;
async function FastGlob(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Promise {
assertPatternsInput(source);
const works = getWorks(source, ProviderAsync, options);
const result = await Promise.all(works);
return utils.array.flatten(result);
}
// https://github.com/typescript-eslint/typescript-eslint/issues/60
// eslint-disable-next-line no-redeclare
namespace FastGlob {
export type Options = OptionsInternal;
export type Entry = EntryInternal;
export type Task = taskManager.Task;
export type Pattern = PatternInternal;
export type FileSystemAdapter = FileSystemAdapterInternal;
export function sync(source: PatternInternal | PatternInternal[], options: OptionsInternal & EntryObjectPredicate): EntryInternal[];
export function sync(source: PatternInternal | PatternInternal[], options?: OptionsInternal): string[];
export function sync(source: PatternInternal | PatternInternal[], options?: OptionsInternal): EntryItem[] {
assertPatternsInput(source);
const works = getWorks(source, ProviderSync, options);
return utils.array.flatten(works);
}
export function stream(source: PatternInternal | PatternInternal[], options?: OptionsInternal): NodeJS.ReadableStream {
assertPatternsInput(source);
const works = getWorks(source, ProviderStream, options);
/**
* The stream returned by the provider cannot work with an asynchronous iterator.
* To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams.
* This affects performance (+25%). I don't see best solution right now.
*/
return utils.stream.merge(works);
}
export function generateTasks(source: PatternInternal | PatternInternal[], options?: OptionsInternal): Task[] {
assertPatternsInput(source);
const patterns = patternManager.transform(([] as PatternInternal[]).concat(source));
const settings = new Settings(options);
return taskManager.generate(patterns, settings);
}
export function isDynamicPattern(source: PatternInternal, options?: OptionsInternal): boolean {
assertPatternsInput(source);
const settings = new Settings(options);
return utils.pattern.isDynamicPattern(source, settings);
}
export function escapePath(source: PatternInternal): PatternInternal {
assertPatternsInput(source);
return utils.path.escape(source);
}
}
function getWorks(source: PatternInternal | PatternInternal[], _Provider: new (settings: Settings) => Provider, options?: OptionsInternal): T[] {
const patterns = patternManager.transform(([] as PatternInternal[]).concat(source));
const settings = new Settings(options);
const tasks = taskManager.generate(patterns, settings);
const provider = new _Provider(settings);
return tasks.map(provider.read, provider);
}
function assertPatternsInput(input: unknown): void | never {
const source = ([] as unknown[]).concat(input);
const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item));
if (!isValidSource) {
throw new TypeError('Patterns must be a string (non empty) or an array of strings');
}
}
export = FastGlob;
fast-glob-3.2.12/src/managers/ 0000775 0000000 0000000 00000000000 14306550000 0016032 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/managers/patterns.spec.ts 0000664 0000000 0000000 00000003352 14306550000 0021176 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as manager from './patterns';
describe('Managers → Pattern', () => {
describe('.transform', () => {
it('should transform patterns with duplicate slashes', () => {
const expected = ['a/b', 'b/c'];
const actual = manager.transform(['a/b', 'b//c']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.removeDuplicateSlashes', () => {
it('should do not change patterns', () => {
const action = manager.removeDuplicateSlashes;
assert.strictEqual(action('directory/file.md'), 'directory/file.md');
assert.strictEqual(action('files{.txt,/file.md}'), 'files{.txt,/file.md}');
});
it('should do not change the device path in patterns with UNC parts', () => {
const action = manager.removeDuplicateSlashes;
assert.strictEqual(action('//?//D://'), '//?/D:/');
assert.strictEqual(action('//.//D:///'), '//./D:/');
assert.strictEqual(action('//LOCALHOST//d$//'), '//LOCALHOST/d$/');
assert.strictEqual(action('//127.0.0.1///d$//'), '//127.0.0.1/d$/');
assert.strictEqual(action('//./UNC////LOCALHOST///d$//'), '//./UNC/LOCALHOST/d$/');
});
it('should remove duplicate slashes in the middle and the of the pattern', () => {
const action = manager.removeDuplicateSlashes;
assert.strictEqual(action('a//b'), 'a/b');
assert.strictEqual(action('b///c'), 'b/c');
assert.strictEqual(action('c/d///'), 'c/d/');
assert.strictEqual(action('//?//D://'), '//?/D:/');
});
it('should form double slashes at the beginning of the pattern', () => {
const action = manager.removeDuplicateSlashes;
assert.strictEqual(action('///*'), '//*');
assert.strictEqual(action('////?'), '//?');
assert.strictEqual(action('///?/D:/'), '//?/D:/');
});
});
});
fast-glob-3.2.12/src/managers/patterns.ts 0000664 0000000 0000000 00000001413 14306550000 0020241 0 ustar 00root root 0000000 0000000 /**
* Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string.
* The latter is due to the presence of the device path at the beginning of the UNC path.
* @todo rewrite to negative lookbehind with the next major release.
*/
const DOUBLE_SLASH_RE = /(?!^)\/{2,}/g;
export function transform(patterns: string[]): string[] {
return patterns.map((pattern) => removeDuplicateSlashes(pattern));
}
/**
* This package only works with forward slashes as a path separator.
* Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes.
*/
export function removeDuplicateSlashes(pattern: string): string {
return pattern.replace(DOUBLE_SLASH_RE, '/');
}
fast-glob-3.2.12/src/managers/tasks.spec.ts 0000664 0000000 0000000 00000013001 14306550000 0020453 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import Settings from '../settings';
import * as tests from '../tests';
import { PatternsGroup } from '../types';
import * as manager from './tasks';
describe('Managers → Task', () => {
describe('.generate', () => {
it('should return task with negative patterns from «ignore» option', () => {
const settings = new Settings({ ignore: ['*.txt'] });
const expected = [
tests.task.builder().base('a').positive('a/*').negative('*.md').negative('*.txt').build()
];
const actual = manager.generate(['a/*', '!*.md'], settings);
assert.deepStrictEqual(actual, expected);
});
it('should return static and dynamic tasks', () => {
const settings = new Settings({ ignore: ['*.txt'] });
const expected = [
tests.task.builder().base('a').static().positive('a/file.json').negative('b/*.md').negative('*.txt').build(),
tests.task.builder().base('b').positive('b/*').negative('b/*.md').negative('*.txt').build()
];
const actual = manager.generate(['a/file.json', 'b/*', '!b/*.md'], settings);
assert.deepStrictEqual(actual, expected);
});
it('should return only dynamic tasks when the `caseSensitiveMatch` option is enabled', () => {
const settings = new Settings({ caseSensitiveMatch: false });
const expected = [
tests.task.builder().base('a').positive('a/file.json').negative('b/*.md').build(),
tests.task.builder().base('b').positive('b/*').negative('b/*.md').build()
];
const actual = manager.generate(['a/file.json', 'b/*', '!b/*.md'], settings);
assert.deepStrictEqual(actual, expected);
});
});
describe('.convertPatternsToTasks', () => {
it('should return one task when positive patterns have a global pattern', () => {
const expected = [
tests.task.builder().base('.').positive('*').negative('*.md').build()
];
const actual = manager.convertPatternsToTasks(['*'], ['*.md'], /* dynamic */ true);
assert.deepStrictEqual(actual, expected);
});
it('should return two tasks when one of patterns contains reference to the parent directory', () => {
const expected = [
tests.task.builder().base('..').positive('../*.md').negative('*.md').build(),
tests.task.builder().base('.').positive('*').positive('a/*').negative('*.md').build()
];
const actual = manager.convertPatternsToTasks(['*', 'a/*', '../*.md'], ['*.md'], /* dynamic */ true);
console.dir(actual, { colors: true });
assert.deepStrictEqual(actual, expected);
});
it('should return two tasks when all patterns refers to the different base directories', () => {
const expected = [
tests.task.builder().base('a').positive('a/*').negative('b/*.md').build(),
tests.task.builder().base('b').positive('b/*').negative('b/*.md').build()
];
const actual = manager.convertPatternsToTasks(['a/*', 'b/*'], ['b/*.md'], /* dynamic */ true);
assert.deepStrictEqual(actual, expected);
});
});
describe('.getPositivePatterns', () => {
it('should return only positive patterns', () => {
const expected = ['*'];
const actual = manager.getPositivePatterns(['*', '!*.md']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.getNegativePatternsAsPositive', () => {
it('should return negative patterns as positive', () => {
const expected = ['*.md'];
const actual = manager.getNegativePatternsAsPositive(['*', '!*.md'], []);
assert.deepStrictEqual(actual, expected);
});
it('should return negative patterns as positive with patterns from ignore option', () => {
const expected = ['*.md', '*.txt', '*.json'];
const actual = manager.getNegativePatternsAsPositive(['*', '!*.md'], ['*.txt', '!*.json']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.groupPatternsByBaseDirectory', () => {
it('should return empty object', () => {
const expected: PatternsGroup = {};
const actual = manager.groupPatternsByBaseDirectory([]);
assert.deepStrictEqual(actual, expected);
});
it('should return grouped patterns', () => {
const expected: PatternsGroup = {
'.': ['*'],
a: ['a/*']
};
const actual = manager.groupPatternsByBaseDirectory(['*', 'a/*']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.convertPatternGroupsToTasks', () => {
it('should return two tasks', () => {
const expected = [
tests.task.builder().base('a').positive('a/*').negative('b/*.md').build(),
tests.task.builder().base('b').positive('b/*').negative('b/*.md').build()
];
const actual = manager.convertPatternGroupsToTasks({ a: ['a/*'], b: ['b/*'] }, ['b/*.md'], /* dynamic */ true);
assert.deepStrictEqual(actual, expected);
});
});
describe('.convertPatternGroupToTask', () => {
it('should return created dynamic task', () => {
const expected = tests.task.builder().base('.').positive('*').negative('*.md').build();
const actual = manager.convertPatternGroupToTask('.', ['*'], ['*.md'], /* dynamic */ true);
assert.deepStrictEqual(actual, expected);
});
it('should return created static task', () => {
const expected = tests.task.builder().base('.').static().positive('.gitignore').negative('.git*').build();
const actual = manager.convertPatternGroupToTask('.', ['.gitignore'], ['.git*'], /* dynamic */ false);
assert.deepStrictEqual(actual, expected);
});
it('should normalize the base path', () => {
const expected = tests.task.builder().base('root/directory').build();
const actual = manager.convertPatternGroupToTask('root/directory', [], [], /* dynamic */ true);
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/managers/tasks.ts 0000664 0000000 0000000 00000007112 14306550000 0017530 0 ustar 00root root 0000000 0000000 import Settings from '../settings';
import { Pattern, PatternsGroup } from '../types';
import * as utils from '../utils';
export type Task = {
base: string;
dynamic: boolean;
patterns: Pattern[];
positive: Pattern[];
negative: Pattern[];
};
export function generate(patterns: Pattern[], settings: Settings): Task[] {
const positivePatterns = getPositivePatterns(patterns);
const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore);
const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings));
const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings));
const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false);
const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true);
return staticTasks.concat(dynamicTasks);
}
/**
* Returns tasks grouped by basic pattern directories.
*
* Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately.
* This is necessary because directory traversal starts at the base directory and goes deeper.
*/
export function convertPatternsToTasks(positive: Pattern[], negative: Pattern[], dynamic: boolean): Task[] {
const tasks: Task[] = [];
const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive);
const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive);
const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);
tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
/*
* For the sake of reducing future accesses to the file system, we merge all tasks within the current directory
* into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest.
*/
if ('.' in insideCurrentDirectoryGroup) {
tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic));
} else {
tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
}
return tasks;
}
export function getPositivePatterns(patterns: Pattern[]): Pattern[] {
return utils.pattern.getPositivePatterns(patterns);
}
export function getNegativePatternsAsPositive(patterns: Pattern[], ignore: Pattern[]): Pattern[] {
const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore);
const positive = negative.map(utils.pattern.convertToPositivePattern);
return positive;
}
export function groupPatternsByBaseDirectory(patterns: Pattern[]): PatternsGroup {
const group: PatternsGroup = {};
return patterns.reduce((collection, pattern) => {
const base = utils.pattern.getBaseDirectory(pattern);
if (base in collection) {
collection[base].push(pattern);
} else {
collection[base] = [pattern];
}
return collection;
}, group);
}
export function convertPatternGroupsToTasks(positive: PatternsGroup, negative: Pattern[], dynamic: boolean): Task[] {
return Object.keys(positive).map((base) => {
return convertPatternGroupToTask(base, positive[base], negative, dynamic);
});
}
export function convertPatternGroupToTask(base: string, positive: Pattern[], negative: Pattern[], dynamic: boolean): Task {
return {
dynamic,
positive,
negative,
base,
patterns: ([] as Pattern[]).concat(positive, negative.map(utils.pattern.convertToNegativePattern))
};
}
fast-glob-3.2.12/src/providers/ 0000775 0000000 0000000 00000000000 14306550000 0016252 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/providers/async.spec.ts 0000664 0000000 0000000 00000005046 14306550000 0020675 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as sinon from 'sinon';
import { Task } from '../managers/tasks';
import ReaderStream from '../readers/stream';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import { Entry, EntryItem, ErrnoException } from '../types';
import ReaderAsync from '../readers/async';
import ProviderAsync from './async';
class TestProvider extends ProviderAsync {
protected _reader: ReaderAsync = sinon.createStubInstance(ReaderAsync) as unknown as ReaderAsync;
constructor(options?: Options) {
super(new Settings(options));
}
public get reader(): sinon.SinonStubbedInstance {
return this._reader as unknown as sinon.SinonStubbedInstance;
}
}
function getProvider(options?: Options): TestProvider {
return new TestProvider(options);
}
function getEntries(provider: TestProvider, task: Task, entry: Entry): Promise {
provider.reader.dynamic.resolves([entry]);
provider.reader.static.resolves([entry]);
return provider.read(task);
}
describe('Providers → ProviderAsync', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const provider = getProvider();
assert.ok(provider instanceof ProviderAsync);
});
});
describe('.read', () => {
it('should return entries for dynamic task', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').build();
const expected = ['root/file.txt'];
const actual = await getEntries(provider, task, entry);
assert.strictEqual(provider.reader.dynamic.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
it('should return entries for static task', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').static().positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').build();
const expected = ['root/file.txt'];
const actual = await getEntries(provider, task, entry);
assert.strictEqual(provider.reader.static.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
it('should throw error', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
provider.reader.dynamic.rejects(tests.errno.getEnoent());
try {
await provider.read(task);
throw new Error('Wow');
} catch (error) {
assert.strictEqual((error as ErrnoException).code, 'ENOENT');
}
});
});
});
fast-glob-3.2.12/src/providers/async.ts 0000664 0000000 0000000 00000001451 14306550000 0017740 0 ustar 00root root 0000000 0000000 import { Task } from '../managers/tasks';
import { Entry, EntryItem, ReaderOptions } from '../types';
import ReaderAsync from '../readers/async';
import Provider from './provider';
export default class ProviderAsync extends Provider> {
protected _reader: ReaderAsync = new ReaderAsync(this._settings);
public async read(task: Task): Promise {
const root = this._getRootDirectory(task);
const options = this._getReaderOptions(task);
const entries = await this.api(root, task, options);
return entries.map((entry) => options.transform(entry));
}
public api(root: string, task: Task, options: ReaderOptions): Promise {
if (task.dynamic) {
return this._reader.dynamic(root, options);
}
return this._reader.static(task.patterns, options);
}
}
fast-glob-3.2.12/src/providers/filters/ 0000775 0000000 0000000 00000000000 14306550000 0017722 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/providers/filters/deep.spec.ts 0000664 0000000 0000000 00000012476 14306550000 0022152 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import Settings, { Options } from '../../settings';
import * as tests from '../../tests';
import { EntryFilterFunction, Pattern, Entry } from '../../types';
import DeepFilter from './deep';
type FilterOptions = {
base?: string;
positive: Pattern[];
negative?: Pattern[];
options?: Options;
};
const DIRECTORY_ENTRY_LEVEL_1 = tests.entry.builder().path('root').directory().build();
const DIRECTORY_ENTRY_LEVEL_2 = tests.entry.builder().path('root/directory').directory().build();
const DIRECTORY_ENTRY_LEVEL_3 = tests.entry.builder().path('root/nested/directory').directory().build();
function getDeepFilterInstance(options?: Options): DeepFilter {
const settings = new Settings(options);
return new DeepFilter(settings, {
dot: settings.dot
});
}
function getFilter(options: FilterOptions): EntryFilterFunction {
const base = options.base ?? '.';
const negative = options.negative ?? [];
return getDeepFilterInstance(options.options).getFilter(base, options.positive, negative);
}
function getResult(entry: Entry, options: FilterOptions): boolean {
const filter = getFilter(options);
return filter(entry);
}
function accept(entry: Entry, options: FilterOptions): void {
assert.strictEqual(getResult(entry, options), true);
}
function reject(entry: Entry, options: FilterOptions): void {
assert.strictEqual(getResult(entry, options), false);
}
describe('Providers → Filters → Deep', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const filter = getDeepFilterInstance();
assert.ok(filter instanceof DeepFilter);
});
});
describe('.getFilter', () => {
describe('options.deep', () => {
it('should reject when an option has "0" as value', () => {
reject(DIRECTORY_ENTRY_LEVEL_1, {
positive: ['**/*'],
options: { deep: 0 }
});
});
it('should reject when the depth of entry is greater than an allowable value (without base)', () => {
reject(DIRECTORY_ENTRY_LEVEL_3, {
positive: ['**/*'],
options: { deep: 1 }
});
});
it('should reject when the depth of entry is greater than an allowable value (with base as current level)', () => {
reject(DIRECTORY_ENTRY_LEVEL_3, {
positive: ['**/*'],
options: { deep: 1 }
});
});
it('should reject when the depth of entry is greater than an allowable value (with nested base)', () => {
reject(DIRECTORY_ENTRY_LEVEL_3, {
base: 'root/a',
positive: ['root/a/*'],
options: { deep: 1 }
});
});
it('should accept when an option has "Infinity" as value', () => {
accept(DIRECTORY_ENTRY_LEVEL_1, {
positive: ['**/*'],
options: { deep: Infinity }
});
});
});
describe('options.followSymbolicLinks', () => {
it('should reject when an entry is symbolic link and option is disabled', () => {
const entry = tests.entry.builder().path('root').directory().symlink().build();
reject(entry, {
positive: ['**/*'],
options: { followSymbolicLinks: false }
});
});
it('should accept when an entry is symbolic link and option is enabled', () => {
const entry = tests.entry.builder().path('root').directory().symlink().build();
accept(entry, {
positive: ['**/*'],
options: { followSymbolicLinks: true }
});
});
});
describe('Positive pattern', () => {
it('should reject when an entry does not match to the positive pattern', () => {
reject(DIRECTORY_ENTRY_LEVEL_1, {
positive: ['non-root/*']
});
});
it('should reject when an entry starts with leading dot and does not match to the positive pattern', () => {
const entry = tests.entry.builder().path('./root').directory().build();
reject(entry, {
positive: ['non-root/*']
});
});
it('should accept when an entry match to the positive pattern with leading dot', () => {
const entry = tests.entry.builder().path('./root').directory().build();
accept(entry, {
positive: ['./root/*']
});
});
it('should accept when the positive pattern does not match by level, but the "baseNameMatch" is enabled', () => {
accept(DIRECTORY_ENTRY_LEVEL_2, {
positive: ['*'],
options: { baseNameMatch: true }
});
});
it('should accept when the positive pattern has a globstar', () => {
accept(DIRECTORY_ENTRY_LEVEL_3, {
positive: ['**/*']
});
});
});
describe('Negative pattern', () => {
it('should reject when an entry match to the negative pattern', () => {
reject(DIRECTORY_ENTRY_LEVEL_2, {
positive: ['**/*'],
negative: ['root/**']
});
});
it('should accept when the negative pattern has no effect to depth reading', () => {
accept(DIRECTORY_ENTRY_LEVEL_3, {
positive: ['**/*'],
negative: ['**/*']
});
});
it('should accept when an entry does not match to the negative pattern', () => {
accept(DIRECTORY_ENTRY_LEVEL_3, {
positive: ['**/*'],
negative: ['non-root/**/*']
});
});
});
});
describe('Immutability', () => {
it('should return the data without changes', () => {
const filter = getFilter({
positive: ['**/*']
});
const reference = tests.entry.builder().path('root/directory').directory().build();
const entry = tests.entry.builder().path('root/directory').directory().build();
filter(entry);
assert.deepStrictEqual(entry, reference);
});
});
});
fast-glob-3.2.12/src/providers/filters/deep.ts 0000664 0000000 0000000 00000005024 14306550000 0021210 0 ustar 00root root 0000000 0000000 import { Entry, MicromatchOptions, EntryFilterFunction, Pattern, PatternRe } from '../../types';
import Settings from '../../settings';
import * as utils from '../../utils';
import PartialMatcher from '../matchers/partial';
export default class DeepFilter {
constructor(private readonly _settings: Settings, private readonly _micromatchOptions: MicromatchOptions) { }
public getFilter(basePath: string, positive: Pattern[], negative: Pattern[]): EntryFilterFunction {
const matcher = this._getMatcher(positive);
const negativeRe = this._getNegativePatternsRe(negative);
return (entry) => this._filter(basePath, entry, matcher, negativeRe);
}
private _getMatcher(patterns: Pattern[]): PartialMatcher {
return new PartialMatcher(patterns, this._settings, this._micromatchOptions);
}
private _getNegativePatternsRe(patterns: Pattern[]): PatternRe[] {
const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern);
return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions);
}
private _filter(basePath: string, entry: Entry, matcher: PartialMatcher, negativeRe: PatternRe[]): boolean {
if (this._isSkippedByDeep(basePath, entry.path)) {
return false;
}
if (this._isSkippedSymbolicLink(entry)) {
return false;
}
const filepath = utils.path.removeLeadingDotSegment(entry.path);
if (this._isSkippedByPositivePatterns(filepath, matcher)) {
return false;
}
return this._isSkippedByNegativePatterns(filepath, negativeRe);
}
private _isSkippedByDeep(basePath: string, entryPath: string): boolean {
/**
* Avoid unnecessary depth calculations when it doesn't matter.
*/
if (this._settings.deep === Infinity) {
return false;
}
return this._getEntryLevel(basePath, entryPath) >= this._settings.deep;
}
private _getEntryLevel(basePath: string, entryPath: string): number {
const entryPathDepth = entryPath.split('/').length;
if (basePath === '') {
return entryPathDepth;
}
const basePathDepth = basePath.split('/').length;
return entryPathDepth - basePathDepth;
}
private _isSkippedSymbolicLink(entry: Entry): boolean {
return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink();
}
private _isSkippedByPositivePatterns(entryPath: string, matcher: PartialMatcher): boolean {
return !this._settings.baseNameMatch && !matcher.match(entryPath);
}
private _isSkippedByNegativePatterns(entryPath: string, patternsRe: PatternRe[]): boolean {
return !utils.pattern.matchAny(entryPath, patternsRe);
}
}
fast-glob-3.2.12/src/providers/filters/entry.spec.ts 0000664 0000000 0000000 00000014706 14306550000 0022374 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as path from 'path';
import Settings, { Options } from '../../settings';
import * as tests from '../../tests';
import { EntryFilterFunction, Pattern, Entry } from '../../types';
import EntryFilter from './entry';
type FilterOptions = {
positive: Pattern[];
negative?: Pattern[];
options?: Options;
};
const FILE_ENTRY = tests.entry.builder().path('root/file.txt').file().build();
const DIRECTORY_ENTRY = tests.entry.builder().path('root/directory').directory().build();
function getEntryFilterInstance(options?: Options): EntryFilter {
const settings = new Settings(options);
return new EntryFilter(settings, {
dot: settings.dot
});
}
function getFilter(options: FilterOptions): EntryFilterFunction {
const negative = options.negative ?? [];
return getEntryFilterInstance(options.options).getFilter(options.positive, negative);
}
function getResult(entry: Entry, options: FilterOptions): boolean {
const filter = getFilter(options);
return filter(entry);
}
function accept(entry: Entry, options: FilterOptions): void {
assert.strictEqual(getResult(entry, options), true);
}
function reject(entry: Entry, options: FilterOptions): void {
assert.strictEqual(getResult(entry, options), false);
}
describe('Providers → Filters → Entry', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const filter = getEntryFilterInstance();
assert.ok(filter instanceof EntryFilter);
});
});
describe('.getFilter', () => {
describe('options.unique', () => {
it('should do not build the index when an option is disabled', () => {
const filterInstance = getEntryFilterInstance({ unique: false });
const filter = filterInstance.getFilter(['**/*'], []);
filter(FILE_ENTRY);
assert.strictEqual(filterInstance.index.size, 0);
});
it('should do not add an entry to the index when an entry does not match to patterns', () => {
const filterInstance = getEntryFilterInstance();
const filter = filterInstance.getFilter(['**/*.unrelated-file-extension'], []);
filter(FILE_ENTRY);
assert.strictEqual(filterInstance.index.size, 0);
});
it('should reject a duplicate entry', () => {
const filter = getFilter({
positive: ['**/*']
});
filter(FILE_ENTRY);
const actual = filter(FILE_ENTRY);
assert.ok(!actual);
});
it('should accept a duplicate entry when an option is disabled', () => {
const filter = getFilter({
positive: ['**/*'],
options: { unique: false }
});
filter(FILE_ENTRY);
const actual = filter(FILE_ENTRY);
assert.ok(actual);
});
});
describe('options.onlyFiles', () => {
it('should reject a directory entry', () => {
reject(DIRECTORY_ENTRY, {
positive: ['**/*'],
options: { onlyFiles: true }
});
});
it('should accept a directory entry', () => {
accept(DIRECTORY_ENTRY, {
positive: ['**/*'],
options: { onlyFiles: false }
});
});
it('should accept a file entry', () => {
accept(FILE_ENTRY, {
positive: ['**/*'],
options: { onlyFiles: true }
});
});
});
describe('options.onlyDirectories', () => {
it('should reject a file entry', () => {
reject(FILE_ENTRY, {
positive: ['**/*'],
options: { onlyDirectories: true }
});
});
it('should accept a directory entry', () => {
accept(DIRECTORY_ENTRY, {
positive: ['**/*'],
options: { onlyDirectories: true }
});
});
});
describe('options.absolute', () => {
it('should reject when an entry match to the negative pattern', () => {
reject(FILE_ENTRY, {
positive: ['**/*'],
negative: ['**/*'],
options: { absolute: true }
});
});
it('should reject when an entry match to the negative pattern with absolute path', () => {
const negative = path.posix.join(process.cwd().replace(/\\/g, '/'), '**', '*');
reject(FILE_ENTRY, {
positive: ['**/*'],
negative: [negative],
options: { absolute: true }
});
});
it('should accept when an entry does not match to the negative pattern', () => {
accept(FILE_ENTRY, {
positive: ['**/*'],
negative: ['*'],
options: { absolute: true }
});
});
it('should accept when an entry does not match to the negative pattern with absolute path', () => {
const negative = path.posix.join(process.cwd().replace(/\\/g, '/'), 'non-root', '**', '*');
accept(FILE_ENTRY, {
positive: ['**/*'],
negative: [negative],
options: { absolute: true }
});
});
});
describe('options.baseNameMatch', () => {
it('should reject an entry', () => {
reject(FILE_ENTRY, {
positive: ['*'],
options: { baseNameMatch: false }
});
});
it('should accept an entry', () => {
accept(FILE_ENTRY, {
positive: ['*'],
options: { baseNameMatch: true }
});
});
});
describe('Pattern', () => {
it('should reject when an entry match to the negative pattern', () => {
reject(FILE_ENTRY, {
positive: ['**/*'],
negative: ['**/*']
});
});
it('should reject when an entry does not match to the positive pattern', () => {
reject(FILE_ENTRY, {
positive: ['*']
});
});
it('should accept when an entry match to the positive pattern with a leading dot', () => {
accept(FILE_ENTRY, {
positive: ['./**/*']
});
});
it('should accept an entry with a leading dot', () => {
const entry = tests.entry.builder().path('./root/file.txt').file().build();
accept(entry, {
positive: ['**/*']
});
});
it('should accept when an entry match to the positive pattern', () => {
accept(FILE_ENTRY, {
positive: ['**/*']
});
});
it('should try to apply patterns to the path with the trailing slash for directory entry', () => {
accept(DIRECTORY_ENTRY, {
positive: ['**/'],
options: { onlyFiles: false }
});
});
it('should not try to apply patterns to the path with the trailing slash for non-directory entry', () => {
reject(FILE_ENTRY, {
positive: ['**/'],
options: { onlyFiles: false }
});
});
});
});
describe('Immutability', () => {
it('should return the data without changes', () => {
const filter = getFilter({
positive: ['**/*']
});
const reference = tests.entry.builder().path('root/file.txt').file().build();
const entry = tests.entry.builder().path('root/file.txt').file().build();
filter(entry);
assert.deepStrictEqual(entry, reference);
});
});
});
fast-glob-3.2.12/src/providers/filters/entry.ts 0000664 0000000 0000000 00000005312 14306550000 0021434 0 ustar 00root root 0000000 0000000 import Settings from '../../settings';
import { Entry, EntryFilterFunction, MicromatchOptions, Pattern, PatternRe } from '../../types';
import * as utils from '../../utils';
export default class EntryFilter {
public readonly index: Map = new Map();
constructor(private readonly _settings: Settings, private readonly _micromatchOptions: MicromatchOptions) {}
public getFilter(positive: Pattern[], negative: Pattern[]): EntryFilterFunction {
const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions);
const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions);
return (entry) => this._filter(entry, positiveRe, negativeRe);
}
private _filter(entry: Entry, positiveRe: PatternRe[], negativeRe: PatternRe[]): boolean {
if (this._settings.unique && this._isDuplicateEntry(entry)) {
return false;
}
if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) {
return false;
}
if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) {
return false;
}
const filepath = this._settings.baseNameMatch ? entry.name : entry.path;
const isDirectory = entry.dirent.isDirectory();
const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(entry.path, negativeRe, isDirectory);
if (this._settings.unique && isMatched) {
this._createIndexRecord(entry);
}
return isMatched;
}
private _isDuplicateEntry(entry: Entry): boolean {
return this.index.has(entry.path);
}
private _createIndexRecord(entry: Entry): void {
this.index.set(entry.path, undefined);
}
private _onlyFileFilter(entry: Entry): boolean {
return this._settings.onlyFiles && !entry.dirent.isFile();
}
private _onlyDirectoryFilter(entry: Entry): boolean {
return this._settings.onlyDirectories && !entry.dirent.isDirectory();
}
private _isSkippedByAbsoluteNegativePatterns(entryPath: string, patternsRe: PatternRe[]): boolean {
if (!this._settings.absolute) {
return false;
}
const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath);
return utils.pattern.matchAny(fullpath, patternsRe);
}
private _isMatchToPatterns(entryPath: string, patternsRe: PatternRe[], isDirectory: boolean): boolean {
const filepath = utils.path.removeLeadingDotSegment(entryPath);
// Trying to match files and directories by patterns.
const isMatched = utils.pattern.matchAny(filepath, patternsRe);
// A pattern with a trailling slash can be used for directory matching.
// To apply such pattern, we need to add a tralling slash to the path.
if (!isMatched && isDirectory) {
return utils.pattern.matchAny(filepath + '/', patternsRe);
}
return isMatched;
}
}
fast-glob-3.2.12/src/providers/filters/error.spec.ts 0000664 0000000 0000000 00000002440 14306550000 0022354 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import Settings, { Options } from '../../settings';
import * as tests from '../../tests';
import { ErrorFilterFunction } from '../../types';
import ErrorFilter from './error';
function getErrorFilterInstance(options?: Options): ErrorFilter {
const settings = new Settings(options);
return new ErrorFilter(settings);
}
function getFilter(options?: Options): ErrorFilterFunction {
return getErrorFilterInstance(options).getFilter();
}
describe('Providers → Filters → Error', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const filter = getErrorFilterInstance();
assert.ok(filter instanceof ErrorFilter);
});
});
describe('.getFilter', () => {
it('should return true for ENOENT error', () => {
const filter = getFilter();
const actual = filter(tests.errno.getEnoent());
assert.ok(actual);
});
it('should return true for EPERM error when the `suppressErrors` options is enabled', () => {
const filter = getFilter({ suppressErrors: true });
const actual = filter(tests.errno.getEperm());
assert.ok(actual);
});
it('should return false for EPERM error', () => {
const filter = getFilter();
const actual = filter(tests.errno.getEperm());
assert.ok(!actual);
});
});
});
fast-glob-3.2.12/src/providers/filters/error.ts 0000664 0000000 0000000 00000000736 14306550000 0021431 0 ustar 00root root 0000000 0000000 import Settings from '../../settings';
import { ErrnoException, ErrorFilterFunction } from '../../types';
import * as utils from '../../utils';
export default class ErrorFilter {
constructor(private readonly _settings: Settings) { }
public getFilter(): ErrorFilterFunction {
return (error) => this._isNonFatalError(error);
}
private _isNonFatalError(error: ErrnoException): boolean {
return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors;
}
}
fast-glob-3.2.12/src/providers/matchers/ 0000775 0000000 0000000 00000000000 14306550000 0020060 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/providers/matchers/matcher.spec.ts 0000664 0000000 0000000 00000002055 14306550000 0023006 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as tests from '../../tests';
import { Pattern, MicromatchOptions } from '../../types';
import Settings from '../../settings';
import Matcher, { PatternInfo } from './matcher';
class TestMatcher extends Matcher {
public get storage(): PatternInfo[] {
return this._storage;
}
}
function getMatcher(patterns: Pattern[], options: MicromatchOptions = {}): TestMatcher {
return new TestMatcher(patterns, new Settings(), options);
}
describe('Providers → Matchers → Matcher', () => {
describe('.storage', () => {
it('should return created storage', () => {
const matcher = getMatcher(['a*', 'a/**/b']);
const expected: PatternInfo[] = [
tests.pattern.info()
.section(tests.pattern.segment().dynamic().pattern('a*').build())
.build(),
tests.pattern.info()
.section(tests.pattern.segment().pattern('a').build())
.section(tests.pattern.segment().pattern('b').build())
.build()
];
const actual = matcher.storage;
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/providers/matchers/matcher.ts 0000664 0000000 0000000 00000004211 14306550000 0022051 0 ustar 00root root 0000000 0000000 import { Pattern, MicromatchOptions, PatternRe } from '../../types';
import * as utils from '../../utils';
import Settings from '../../settings';
export type PatternSegment = StaticPatternSegment | DynamicPatternSegment;
type StaticPatternSegment = {
dynamic: false;
pattern: Pattern;
};
type DynamicPatternSegment = {
dynamic: true;
pattern: Pattern;
patternRe: PatternRe;
};
export type PatternSection = PatternSegment[];
export type PatternInfo = {
/**
* Indicates that the pattern has a globstar (more than a single section).
*/
complete: boolean;
pattern: Pattern;
segments: PatternSegment[];
sections: PatternSection[];
};
export default abstract class Matcher {
protected readonly _storage: PatternInfo[] = [];
constructor(private readonly _patterns: Pattern[], private readonly _settings: Settings, private readonly _micromatchOptions: MicromatchOptions) {
this._fillStorage();
}
private _fillStorage(): void {
/**
* The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level).
* So, before expand patterns with brace expansion into separated patterns.
*/
const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns);
for (const pattern of patterns) {
const segments = this._getPatternSegments(pattern);
const sections = this._splitSegmentsIntoSections(segments);
this._storage.push({
complete: sections.length <= 1,
pattern,
segments,
sections
});
}
}
private _getPatternSegments(pattern: Pattern): PatternSegment[] {
const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions);
return parts.map((part) => {
const dynamic = utils.pattern.isDynamicPattern(part, this._settings);
if (!dynamic) {
return {
dynamic: false,
pattern: part
};
}
return {
dynamic: true,
pattern: part,
patternRe: utils.pattern.makeRe(part, this._micromatchOptions)
};
});
}
private _splitSegmentsIntoSections(segments: PatternSegment[]): PatternSection[] {
return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern));
}
}
fast-glob-3.2.12/src/providers/matchers/partial.spec.ts 0000664 0000000 0000000 00000003245 14306550000 0023021 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { Pattern, MicromatchOptions } from '../../types';
import Settings from '../../settings';
import Matcher from './partial';
function getMatcher(patterns: Pattern[], options: MicromatchOptions = {}): Matcher {
return new Matcher(patterns, new Settings(), options);
}
function assertMatch(patterns: Pattern[], filepath: string): void | never {
const matcher = getMatcher(patterns);
assert.ok(matcher.match(filepath), `Path "${filepath}" should match: ${patterns}`);
}
function assertNotMatch(patterns: Pattern[], filepath: string): void | never {
const matcher = getMatcher(patterns);
assert.ok(!matcher.match(filepath), `Path "${filepath}" should do not match: ${patterns}`);
}
describe('Providers → Matchers → Partial', () => {
describe('.match', () => {
it('should handle patterns with globstar', () => {
assertMatch(['**'], 'a');
assertMatch(['**'], './a');
assertMatch(['**/a'], 'a');
assertMatch(['**/a'], 'b/a');
assertMatch(['a/**'], 'a/b');
assertNotMatch(['a/**'], 'b');
});
it('should do not match the latest segment', () => {
assertMatch(['b/*'], 'b');
assertNotMatch(['*'], 'a');
assertNotMatch(['a/*'], 'a/b');
});
it('should trying to match all patterns', () => {
assertMatch(['a/*', 'b/*'], 'b');
assertMatch(['non-match/b/c', 'a/*/c'], 'a/b');
assertNotMatch(['non-match/d/c', 'a/b/c'], 'a/d');
});
it('should match a static segment', () => {
assertMatch(['a/b'], 'a');
assertNotMatch(['b/b'], 'a');
});
it('should match a dynamic segment', () => {
assertMatch(['*/b'], 'a');
assertMatch(['{a,b}/*'], 'a');
assertNotMatch(['{a,b}/*'], 'c');
});
});
});
fast-glob-3.2.12/src/providers/matchers/partial.ts 0000664 0000000 0000000 00000002030 14306550000 0022057 0 ustar 00root root 0000000 0000000 import Matcher from './matcher';
export default class PartialMatcher extends Matcher {
public match(filepath: string): boolean {
const parts = filepath.split('/');
const levels = parts.length;
const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels);
for (const pattern of patterns) {
const section = pattern.sections[0];
/**
* In this case, the pattern has a globstar and we must read all directories unconditionally,
* but only if the level has reached the end of the first group.
*
* fixtures/{a,b}/**
* ^ true/false ^ always true
*/
if (!pattern.complete && levels > section.length) {
return true;
}
const match = parts.every((part, index) => {
const segment = pattern.segments[index];
if (segment.dynamic && segment.patternRe.test(part)) {
return true;
}
if (!segment.dynamic && segment.pattern === part) {
return true;
}
return false;
});
if (match) {
return true;
}
}
return false;
}
}
fast-glob-3.2.12/src/providers/provider.spec.ts 0000664 0000000 0000000 00000006370 14306550000 0021413 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as path from 'path';
import { Task } from '../managers/tasks';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import { MicromatchOptions, ReaderOptions } from '../types';
import Provider from './provider';
export class TestProvider extends Provider> {
public read(): Array<{}> {
return [];
}
public getRootDirectory(task: Task): string {
return this._getRootDirectory(task);
}
public getReaderOptions(task: Task): ReaderOptions {
return this._getReaderOptions(task);
}
public getMicromatchOptions(): MicromatchOptions {
return this._getMicromatchOptions();
}
}
export function getProvider(options?: Options): TestProvider {
const settings = new Settings(options);
return new TestProvider(settings);
}
describe('Providers → Provider', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const provider = getProvider();
assert.ok(provider instanceof Provider);
});
});
describe('.getRootDirectory', () => {
it('should return root directory for reader with global base (.)', () => {
const provider = getProvider();
const task = tests.task.builder().base('.').build();
const expected = process.cwd();
const actual = provider.getRootDirectory(task);
assert.strictEqual(actual, expected);
});
it('should return root directory for reader with non-global base (fixtures)', () => {
const provider = getProvider();
const task = tests.task.builder().base('root').build();
const expected = path.join(process.cwd(), 'root');
const actual = provider.getRootDirectory(task);
assert.strictEqual(actual, expected);
});
});
describe('.getReaderOptions', () => {
it('should return options for reader with global base (.)', () => {
const settings = new Settings();
const provider = getProvider(settings);
const task = tests.task.builder().base('.').positive('*').build();
const actual = provider.getReaderOptions(task);
assert.strictEqual(actual.basePath, '');
assert.strictEqual(actual.concurrency, settings.concurrency);
assert.strictEqual(typeof actual.deepFilter, 'function');
assert.strictEqual(typeof actual.entryFilter, 'function');
assert.strictEqual(typeof actual.errorFilter, 'function');
assert.ok(actual.followSymbolicLinks);
assert.strictEqual(typeof actual.fs, 'object');
assert.ok(!actual.stats);
assert.ok(actual.throwErrorOnBrokenSymbolicLink === false);
assert.strictEqual(typeof actual.transform, 'function');
});
it('should return options for reader with non-global base', () => {
const provider = getProvider();
const task = tests.task.builder().base('root').positive('*').build();
const actual = provider.getReaderOptions(task);
assert.strictEqual(actual.basePath, 'root');
});
});
describe('.getMicromatchOptions', () => {
it('should return options for micromatch', () => {
const provider = getProvider();
const expected: MicromatchOptions = {
dot: false,
matchBase: false,
nobrace: false,
nocase: false,
noext: false,
noglobstar: false,
posix: true,
strictSlashes: false
};
const actual = provider.getMicromatchOptions();
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/providers/provider.ts 0000664 0000000 0000000 00000003737 14306550000 0020466 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import { Task } from '../managers/tasks';
import Settings from '../settings';
import { MicromatchOptions, ReaderOptions } from '../types';
import DeepFilter from './filters/deep';
import EntryFilter from './filters/entry';
import ErrorFilter from './filters/error';
import EntryTransformer from './transformers/entry';
export default abstract class Provider {
public readonly errorFilter: ErrorFilter = new ErrorFilter(this._settings);
public readonly entryFilter: EntryFilter = new EntryFilter(this._settings, this._getMicromatchOptions());
public readonly deepFilter: DeepFilter = new DeepFilter(this._settings, this._getMicromatchOptions());
public readonly entryTransformer: EntryTransformer = new EntryTransformer(this._settings);
constructor(protected readonly _settings: Settings) { }
public abstract read(_task: Task): T;
protected _getRootDirectory(task: Task): string {
return path.resolve(this._settings.cwd, task.base);
}
protected _getReaderOptions(task: Task): ReaderOptions {
const basePath = task.base === '.' ? '' : task.base;
return {
basePath,
pathSegmentSeparator: '/',
concurrency: this._settings.concurrency,
deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative),
entryFilter: this.entryFilter.getFilter(task.positive, task.negative),
errorFilter: this.errorFilter.getFilter(),
followSymbolicLinks: this._settings.followSymbolicLinks,
fs: this._settings.fs,
stats: this._settings.stats,
throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink,
transform: this.entryTransformer.getTransformer()
};
}
protected _getMicromatchOptions(): MicromatchOptions {
return {
dot: this._settings.dot,
matchBase: this._settings.baseNameMatch,
nobrace: !this._settings.braceExpansion,
nocase: !this._settings.caseSensitiveMatch,
noext: !this._settings.extglob,
noglobstar: !this._settings.globstar,
posix: true,
strictSlashes: false
};
}
}
fast-glob-3.2.12/src/providers/stream.spec.ts 0000664 0000000 0000000 00000006650 14306550000 0021055 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { PassThrough } from 'stream';
import * as sinon from 'sinon';
import { Task } from '../managers/tasks';
import ReaderStream from '../readers/stream';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import { Entry, EntryItem, ErrnoException } from '../types';
import ProviderStream from './stream';
class TestProvider extends ProviderStream {
protected _reader: ReaderStream = sinon.createStubInstance(ReaderStream) as unknown as ReaderStream;
constructor(options?: Options) {
super(new Settings(options));
}
public get reader(): sinon.SinonStubbedInstance {
return this._reader as unknown as sinon.SinonStubbedInstance;
}
}
function getProvider(options?: Options): TestProvider {
return new TestProvider(options);
}
function getEntries(provider: TestProvider, task: Task, entry: Entry): Promise {
const reader = new PassThrough({ objectMode: true });
provider.reader.dynamic.returns(reader);
provider.reader.static.returns(reader);
reader.push(entry);
reader.push(null);
return new Promise((resolve, reject) => {
const items: EntryItem[] = [];
const api = provider.read(task);
api.on('data', (item: EntryItem) => items.push(item));
api.once('error', reject);
api.once('end', () => resolve(items));
});
}
describe('Providers → ProviderStream', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const provider = getProvider();
assert.ok(provider instanceof ProviderStream);
});
});
describe('.read', () => {
it('should return entries for dynamic entries', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();
const expected = ['root/file.txt'];
const actual = await getEntries(provider, task, entry);
assert.strictEqual(provider.reader.dynamic.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
it('should return entries for static entries', async () => {
const provider = getProvider();
const task = tests.task.builder().base('.').static().positive('root/file.txt').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();
const expected = ['root/file.txt'];
const actual = await getEntries(provider, task, entry);
assert.strictEqual(provider.reader.static.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
it('should emit error to the transform stream', (done) => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
const stream = new PassThrough({
read(): void {
stream.emit('error', tests.errno.getEnoent());
}
});
provider.reader.dynamic.returns(stream);
const actual = provider.read(task);
actual.once('error', (error: ErrnoException) => {
assert.strictEqual(error.code, 'ENOENT');
done();
});
});
it('should destroy source stream when the destination stream is closed', (done) => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
const stream = new PassThrough();
provider.reader.dynamic.returns(stream);
const actual = provider.read(task);
actual.once('close', () => {
assert.ok(stream.destroyed);
done();
});
actual.emit('close');
});
});
});
fast-glob-3.2.12/src/providers/stream.ts 0000664 0000000 0000000 00000002163 14306550000 0020117 0 ustar 00root root 0000000 0000000 import { Readable } from 'stream';
import { Task } from '../managers/tasks';
import ReaderStream from '../readers/stream';
import { Entry, ErrnoException, ReaderOptions } from '../types';
import Provider from './provider';
export default class ProviderStream extends Provider {
protected _reader: ReaderStream = new ReaderStream(this._settings);
public read(task: Task): Readable {
const root = this._getRootDirectory(task);
const options = this._getReaderOptions(task);
const source = this.api(root, task, options);
const destination = new Readable({ objectMode: true, read: () => { /* noop */ } });
source
.once('error', (error: ErrnoException) => destination.emit('error', error))
.on('data', (entry: Entry) => destination.emit('data', options.transform(entry)))
.once('end', () => destination.emit('end'));
destination
.once('close', () => source.destroy());
return destination;
}
public api(root: string, task: Task, options: ReaderOptions): Readable {
if (task.dynamic) {
return this._reader.dynamic(root, options);
}
return this._reader.static(task.patterns, options);
}
}
fast-glob-3.2.12/src/providers/sync.spec.ts 0000664 0000000 0000000 00000003565 14306550000 0020540 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as sinon from 'sinon';
import ReaderSync from '../readers/sync';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import ProviderSync from './sync';
class TestProvider extends ProviderSync {
protected _reader: ReaderSync = sinon.createStubInstance(ReaderSync) as unknown as ReaderSync;
constructor(options?: Options) {
super(new Settings(options));
}
public get reader(): sinon.SinonStubbedInstance {
return this._reader as unknown as sinon.SinonStubbedInstance;
}
}
function getProvider(options?: Options): TestProvider {
return new TestProvider(options);
}
describe('Providers → ProviderSync', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const provider = getProvider();
assert.ok(provider instanceof ProviderSync);
});
});
describe('.read', () => {
it('should return entries for dynamic task', () => {
const provider = getProvider();
const task = tests.task.builder().base('.').positive('*').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();
provider.reader.dynamic.returns([entry]);
const expected = ['root/file.txt'];
const actual = provider.read(task);
assert.strictEqual(provider.reader.dynamic.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
it('should return entries for static task', () => {
const provider = getProvider();
const task = tests.task.builder().base('.').static().positive('root/file.txt').build();
const entry = tests.entry.builder().path('root/file.txt').file().build();
provider.reader.static.returns([entry]);
const expected = ['root/file.txt'];
const actual = provider.read(task);
assert.strictEqual(provider.reader.static.callCount, 1);
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/providers/sync.ts 0000664 0000000 0000000 00000001353 14306550000 0017600 0 ustar 00root root 0000000 0000000 import { Task } from '../managers/tasks';
import ReaderSync from '../readers/sync';
import { Entry, EntryItem, ReaderOptions } from '../types';
import Provider from './provider';
export default class ProviderSync extends Provider {
protected _reader: ReaderSync = new ReaderSync(this._settings);
public read(task: Task): EntryItem[] {
const root = this._getRootDirectory(task);
const options = this._getReaderOptions(task);
const entries = this.api(root, task, options);
return entries.map(options.transform);
}
public api(root: string, task: Task, options: ReaderOptions): Entry[] {
if (task.dynamic) {
return this._reader.dynamic(root, options);
}
return this._reader.static(task.patterns, options);
}
}
fast-glob-3.2.12/src/providers/transformers/ 0000775 0000000 0000000 00000000000 14306550000 0020777 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/providers/transformers/entry.spec.ts 0000664 0000000 0000000 00000006647 14306550000 0023456 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as path from 'path';
import Settings, { Options } from '../../settings';
import * as tests from '../../tests';
import { EntryTransformerFunction } from '../../types';
import * as utils from '../../utils';
import EntryTransformer from './entry';
function getEntryTransformer(options?: Options): EntryTransformer {
return new EntryTransformer(new Settings(options));
}
function getTransformer(options?: Options): EntryTransformerFunction {
return getEntryTransformer(options).getTransformer();
}
describe('Providers → Transformers → Entry', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const filter = getEntryTransformer();
assert.ok(filter instanceof EntryTransformer);
});
});
describe('.getTransformer', () => {
it('should return transformed entry as string when options is not provided', () => {
const transformer = getTransformer();
const entry = tests.entry.builder().path('root/file.txt').file().build();
const expected = 'root/file.txt';
const actual = transformer(entry);
assert.strictEqual(actual, expected);
});
it('should return transformed entry as object when the `objectMode` option is enabled', () => {
const transformer = getTransformer({ objectMode: true });
const entry = tests.entry.builder().path('root/file.txt').file().build();
const expected = entry;
const actual = transformer(entry);
assert.deepStrictEqual(actual, expected);
});
it('should return transformed entry as object when the `stats` option is enabled', () => {
const transformer = getTransformer({ stats: true });
const entry = tests.entry.builder().path('root/file.txt').file().stats().build();
const expected = entry;
const actual = transformer(entry);
assert.deepStrictEqual(actual, expected);
});
it('should return entry with absolute filepath when the `absolute` option is enabled', () => {
const transformer = getTransformer({ absolute: true });
const entry = tests.entry.builder().path('root/file.txt').file().build();
const fullpath = path.join(process.cwd(), 'root', 'file.txt');
const expected = utils.path.unixify(fullpath);
const actual = transformer(entry);
assert.strictEqual(actual, expected);
});
it('should return entry with trailing slash when the `markDirectories` is enabled', () => {
const transformer = getTransformer({ markDirectories: true });
const entry = tests.entry.builder().path('root/directory').directory().build();
const expected = 'root/directory/';
const actual = transformer(entry);
assert.strictEqual(actual, expected);
});
it('should return correct entry when the `absolute` and `markDirectories` options is enabled', () => {
const transformer = getTransformer({ absolute: true, markDirectories: true });
const entry = tests.entry.builder().path('root/directory').directory().build();
const fullpath = path.join(process.cwd(), 'root', 'directory', '/');
const expected = utils.path.unixify(fullpath);
const actual = transformer(entry);
assert.strictEqual(actual, expected);
});
it('should do not mutate the entry when the `markDirectories` option is enabled', () => {
const transformer = getTransformer({ markDirectories: true });
const entry = tests.entry.builder().path('root/directory').directory().build();
const actual = transformer(entry);
assert.notStrictEqual(actual, entry.path);
});
});
});
fast-glob-3.2.12/src/providers/transformers/entry.ts 0000664 0000000 0000000 00000001421 14306550000 0022506 0 ustar 00root root 0000000 0000000 import Settings from '../../settings';
import { Entry, EntryItem, EntryTransformerFunction } from '../../types';
import * as utils from '../../utils';
export default class EntryTransformer {
constructor(private readonly _settings: Settings) { }
public getTransformer(): EntryTransformerFunction {
return (entry) => this._transform(entry);
}
private _transform(entry: Entry): EntryItem {
let filepath = entry.path;
if (this._settings.absolute) {
filepath = utils.path.makeAbsolute(this._settings.cwd, filepath);
filepath = utils.path.unixify(filepath);
}
if (this._settings.markDirectories && entry.dirent.isDirectory()) {
filepath += '/';
}
if (!this._settings.objectMode) {
return filepath;
}
return {
...entry,
path: filepath
};
}
}
fast-glob-3.2.12/src/readers/ 0000775 0000000 0000000 00000000000 14306550000 0015662 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/readers/async.spec.ts 0000664 0000000 0000000 00000004237 14306550000 0020306 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { PassThrough } from 'stream';
import * as sinon from 'sinon';
import * as fsWalk from '@nodelib/fs.walk';
import Settings, { Options } from '../settings';
import { ReaderOptions } from '../types';
import * as tests from '../tests';
import ReaderAsync from './async';
import ReaderStream from './stream';
type WalkSignature = typeof fsWalk.walk;
class TestReader extends ReaderAsync {
protected _walkAsync: WalkSignature = sinon.stub() as unknown as WalkSignature;
protected _readerStream: ReaderStream = sinon.createStubInstance(ReaderStream) as unknown as ReaderStream;
constructor(options?: Options) {
super(new Settings(options));
}
public get walkAsync(): sinon.SinonStub {
return this._walkAsync as unknown as sinon.SinonStub;
}
public get readerStream(): sinon.SinonStubbedInstance {
return this._readerStream as unknown as sinon.SinonStubbedInstance;
}
}
function getReader(options?: Options): TestReader {
return new TestReader(options);
}
function getReaderOptions(options: Partial = {}): ReaderOptions {
return { ...options } as unknown as ReaderOptions;
}
describe('Readers → ReaderAsync', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const reader = getReader();
assert.ok(reader instanceof TestReader);
});
});
describe('.dynamic', () => {
it('should call fs.walk method', async () => {
const reader = getReader();
const readerOptions = getReaderOptions();
reader.walkAsync.yields(null, []);
await reader.dynamic('root', readerOptions);
assert.ok(reader.walkAsync.called);
});
});
describe('.static', () => {
it('should call stream reader method', async () => {
const entry = tests.entry.builder().path('root/file.txt').build();
const reader = getReader();
const readerOptions = getReaderOptions();
const readerStream = new PassThrough({ objectMode: true });
readerStream.push(entry);
readerStream.push(null);
reader.readerStream.static.returns(readerStream);
await reader.static(['a.txt'], readerOptions);
assert.ok(reader.readerStream.static.called);
});
});
});
fast-glob-3.2.12/src/readers/async.ts 0000664 0000000 0000000 00000002100 14306550000 0017340 0 ustar 00root root 0000000 0000000 import * as fsWalk from '@nodelib/fs.walk';
import { Entry, ReaderOptions, Pattern } from '../types';
import Reader from './reader';
import ReaderStream from './stream';
export default class ReaderAsync extends Reader> {
protected _walkAsync: typeof fsWalk.walk = fsWalk.walk;
protected _readerStream: ReaderStream = new ReaderStream(this._settings);
public dynamic(root: string, options: ReaderOptions): Promise {
return new Promise((resolve, reject) => {
this._walkAsync(root, options, (error, entries) => {
if (error === null) {
resolve(entries);
} else {
reject(error);
}
});
});
}
public async static(patterns: Pattern[], options: ReaderOptions): Promise {
const entries: Entry[] = [];
const stream = this._readerStream.static(patterns, options);
// After #235, replace it with an asynchronous iterator.
return new Promise((resolve, reject) => {
stream.once('error', reject);
stream.on('data', (entry: Entry) => entries.push(entry));
stream.once('end', () => resolve(entries));
});
}
}
fast-glob-3.2.12/src/readers/reader.spec.ts 0000664 0000000 0000000 00000003457 14306550000 0020436 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as fs from 'fs';
import * as path from 'path';
import { Stats } from '@nodelib/fs.macchiato';
import Settings, { Options } from '../settings';
import { Entry, Pattern } from '../types';
import Reader from './reader';
class TestReader extends Reader {
constructor(options?: Options) {
super(new Settings(options));
}
public dynamic(): never[] {
return [];
}
public static(): never[] {
return [];
}
public getFullEntryPath(filepath: string): string {
return this._getFullEntryPath(filepath);
}
public makeEntry(stats: fs.Stats, pattern: Pattern): Entry {
return this._makeEntry(stats, pattern);
}
}
function getReader(options?: Options): TestReader {
return new TestReader(options);
}
describe('Readers → Reader', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const reader = getReader();
assert.ok(reader instanceof TestReader);
});
});
describe('.getFullEntryPath', () => {
it('should return path to entry', () => {
const reader = getReader();
const expected = path.join(process.cwd(), 'config.json');
const actual = reader.getFullEntryPath('config.json');
assert.strictEqual(actual, expected);
});
});
describe('.makeEntry', () => {
it('should return created entry', () => {
const reader = getReader();
const pattern = 'config.json';
const actual = reader.makeEntry(new Stats(), pattern);
assert.strictEqual(actual.name, pattern);
assert.strictEqual(actual.path, pattern);
assert.ok(actual.dirent.isFile());
});
it('should return created entry with fs.Stats', () => {
const reader = getReader({ stats: true });
const pattern = 'config.json';
const actual = reader.makeEntry(new Stats(), pattern);
assert.ok(actual.stats);
});
});
});
fast-glob-3.2.12/src/readers/reader.ts 0000664 0000000 0000000 00000002346 14306550000 0017501 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import * as path from 'path';
import * as fsStat from '@nodelib/fs.stat';
import Settings from '../settings';
import { Entry, ErrnoException, Pattern, ReaderOptions } from '../types';
import * as utils from '../utils';
export default abstract class Reader {
protected readonly _fsStatSettings: fsStat.Settings = new fsStat.Settings({
followSymbolicLink: this._settings.followSymbolicLinks,
fs: this._settings.fs,
throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks
});
constructor(protected readonly _settings: Settings) { }
public abstract dynamic(root: string, options: ReaderOptions): T;
public abstract static(patterns: Pattern[], options: ReaderOptions): T;
protected _getFullEntryPath(filepath: string): string {
return path.resolve(this._settings.cwd, filepath);
}
protected _makeEntry(stats: fs.Stats, pattern: Pattern): Entry {
const entry: Entry = {
name: pattern,
path: pattern,
dirent: utils.fs.createDirentFromStats(pattern, stats)
};
if (this._settings.stats) {
entry.stats = stats;
}
return entry;
}
protected _isFatalError(error: ErrnoException): boolean {
return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors;
}
}
fast-glob-3.2.12/src/readers/stream.spec.ts 0000664 0000000 0000000 00000007655 14306550000 0020473 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { Stats } from '@nodelib/fs.macchiato';
import * as fsStat from '@nodelib/fs.stat';
import * as fsWalk from '@nodelib/fs.walk';
import * as sinon from 'sinon';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import { Entry, ErrnoException, ReaderOptions } from '../types';
import ReaderStream from './stream';
type WalkSignature = typeof fsWalk.walkStream;
type StatSignature = typeof fsStat.stat;
class TestReader extends ReaderStream {
protected _walkStream: WalkSignature = sinon.stub() as unknown as WalkSignature;
protected _stat: StatSignature = sinon.stub() as unknown as StatSignature;
constructor(options?: Options) {
super(new Settings(options));
}
public get walkStream(): sinon.SinonStub {
return this._walkStream as unknown as sinon.SinonStub;
}
public get stat(): sinon.SinonStub {
return this._stat as unknown as sinon.SinonStub;
}
}
function getReader(options?: Options): TestReader {
return new TestReader(options);
}
function getReaderOptions(options: Partial = {}): ReaderOptions {
return { ...options } as unknown as ReaderOptions;
}
describe('Readers → ReaderStream', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const reader = getReader();
assert.ok(reader instanceof TestReader);
});
});
describe('.dynamic', () => {
it('should call fs.walk method', () => {
const reader = getReader();
const readerOptions = getReaderOptions();
reader.dynamic('root', readerOptions);
assert.ok(reader.walkStream.called);
});
});
describe('.static', () => {
it('should return entries', (done) => {
const reader = getReader();
const readerOptions = getReaderOptions({ entryFilter: () => true });
reader.stat.onFirstCall().yields(null, new Stats());
reader.stat.onSecondCall().yields(null, new Stats());
const entries: Entry[] = [];
const stream = reader.static(['a.txt', 'b.txt'], readerOptions);
stream.on('data', (entry: Entry) => entries.push(entry));
stream.once('end', () => {
assert.strictEqual(entries[0].name, 'a.txt');
assert.strictEqual(entries[1].name, 'b.txt');
done();
});
});
it('should throw an error when the filter does not suppress the error', (done) => {
const reader = getReader();
const readerOptions = getReaderOptions({
errorFilter: () => false,
entryFilter: () => true
});
reader.stat.onFirstCall().yields(tests.errno.getEperm());
reader.stat.onSecondCall().yields(null, new Stats());
const entries: Entry[] = [];
const stream = reader.static(['a.txt', 'b.txt'], readerOptions);
stream.on('data', (entry: Entry) => entries.push(entry));
stream.once('error', (error: ErrnoException) => {
assert.strictEqual(error.code, 'EPERM');
done();
});
});
it('should do not throw an error when the filter suppress the error', (done) => {
const reader = getReader();
const readerOptions = getReaderOptions({
errorFilter: () => true,
entryFilter: () => true
});
reader.stat.onFirstCall().yields(tests.errno.getEnoent());
reader.stat.onSecondCall().yields(null, new Stats());
const entries: Entry[] = [];
const stream = reader.static(['a.txt', 'b.txt'], readerOptions);
stream.on('data', (entry: Entry) => entries.push(entry));
stream.once('end', () => {
assert.strictEqual(entries.length, 1);
assert.strictEqual(entries[0].name, 'b.txt');
done();
});
});
it('should do not include entry when the filter excludes it', (done) => {
const reader = getReader();
const readerOptions = getReaderOptions({ entryFilter: () => false });
reader.stat.yields(null, new Stats());
const entries: Entry[] = [];
const stream = reader.static(['a.txt'], readerOptions);
stream.on('data', (entry: Entry) => entries.push(entry));
stream.once('end', () => {
assert.strictEqual(entries.length, 0);
done();
});
});
});
});
fast-glob-3.2.12/src/readers/stream.ts 0000664 0000000 0000000 00000003405 14306550000 0017527 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import { PassThrough, Readable } from 'stream';
import * as fsStat from '@nodelib/fs.stat';
import * as fsWalk from '@nodelib/fs.walk';
import { Entry, ErrnoException, Pattern, ReaderOptions } from '../types';
import Reader from './reader';
export default class ReaderStream extends Reader {
protected _walkStream: typeof fsWalk.walkStream = fsWalk.walkStream;
protected _stat: typeof fsStat.stat = fsStat.stat;
public dynamic(root: string, options: ReaderOptions): Readable {
return this._walkStream(root, options);
}
public static(patterns: Pattern[], options: ReaderOptions): Readable {
const filepaths = patterns.map(this._getFullEntryPath, this);
const stream = new PassThrough({ objectMode: true });
stream._write = (index: number, _enc, done) => {
return this._getEntry(filepaths[index], patterns[index], options)
.then((entry) => {
if (entry !== null && options.entryFilter(entry)) {
stream.push(entry);
}
if (index === filepaths.length - 1) {
stream.end();
}
done();
})
.catch(done);
};
for (let i = 0; i < filepaths.length; i++) {
stream.write(i);
}
return stream;
}
private _getEntry(filepath: string, pattern: Pattern, options: ReaderOptions): Promise {
return this._getStat(filepath)
.then((stats) => this._makeEntry(stats, pattern))
.catch((error: ErrnoException) => {
if (options.errorFilter(error)) {
return null;
}
throw error;
});
}
private _getStat(filepath: string): Promise {
return new Promise((resolve, reject) => {
this._stat(filepath, this._fsStatSettings, (error: NodeJS.ErrnoException | null, stats) => {
return error === null ? resolve(stats) : reject(error);
});
});
}
}
fast-glob-3.2.12/src/readers/sync.spec.ts 0000664 0000000 0000000 00000006546 14306550000 0020152 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { Stats } from '@nodelib/fs.macchiato';
import * as fsStat from '@nodelib/fs.stat';
import * as fsWalk from '@nodelib/fs.walk';
import * as sinon from 'sinon';
import Settings, { Options } from '../settings';
import * as tests from '../tests';
import { ReaderOptions } from '../types';
import ReaderSync from './sync';
type WalkSignature = typeof fsWalk.walkSync;
type StatSignature = typeof fsStat.statSync;
class TestReader extends ReaderSync {
protected _walkSync: WalkSignature = sinon.stub() as unknown as WalkSignature;
protected _statSync: StatSignature = sinon.stub() as unknown as StatSignature;
constructor(options?: Options) {
super(new Settings(options));
}
public get walkSync(): sinon.SinonStub {
return this._walkSync as unknown as sinon.SinonStub;
}
public get statSync(): sinon.SinonStub {
return this._statSync as unknown as sinon.SinonStub;
}
}
function getReader(options?: Options): TestReader {
return new TestReader(options);
}
function getReaderOptions(options: Partial = {}): ReaderOptions {
return { ...options } as unknown as ReaderOptions;
}
describe('Readers → ReaderSync', () => {
describe('Constructor', () => {
it('should create instance of class', () => {
const reader = getReader();
assert.ok(reader instanceof TestReader);
});
});
describe('.dynamic', () => {
it('should call fs.walk method', () => {
const reader = getReader();
const readerOptions = getReaderOptions();
reader.dynamic('root', readerOptions);
assert.ok(reader.walkSync.called);
});
});
describe('.static', () => {
it('should return entries', () => {
const reader = getReader();
const readerOptions = getReaderOptions({ entryFilter: () => true });
reader.statSync.onFirstCall().returns(new Stats());
reader.statSync.onSecondCall().returns(new Stats());
const actual = reader.static(['a.txt', 'b.txt'], readerOptions);
assert.strictEqual(actual[0].name, 'a.txt');
assert.strictEqual(actual[1].name, 'b.txt');
});
it('should throw an error when the filter does not suppress the error', () => {
const reader = getReader();
const readerOptions = getReaderOptions({
errorFilter: () => false,
entryFilter: () => true
});
reader.statSync.onFirstCall().throws(tests.errno.getEperm());
reader.statSync.onSecondCall().returns(new Stats());
const expectedErrorMessageRe = /Error: EPERM: operation not permitted/;
assert.throws(() => reader.static(['a.txt', 'b.txt'], readerOptions), expectedErrorMessageRe);
});
it('should do not throw an error when the filter suppress the error', () => {
const reader = getReader();
const readerOptions = getReaderOptions({
errorFilter: () => true,
entryFilter: () => true
});
reader.statSync.onFirstCall().throws(tests.errno.getEnoent());
reader.statSync.onSecondCall().returns(new Stats());
const actual = reader.static(['a.txt', 'b.txt'], readerOptions);
assert.strictEqual(actual.length, 1);
assert.strictEqual(actual[0].name, 'b.txt');
});
it('should do not include entry when the filter excludes it', () => {
const reader = getReader();
const readerOptions = getReaderOptions({ entryFilter: () => false });
reader.statSync.returns(new Stats());
const actual = reader.static(['a.txt'], readerOptions);
assert.strictEqual(actual.length, 0);
});
});
});
fast-glob-3.2.12/src/readers/sync.ts 0000664 0000000 0000000 00000002450 14306550000 0017207 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import * as fsStat from '@nodelib/fs.stat';
import * as fsWalk from '@nodelib/fs.walk';
import { Entry, ErrnoException, Pattern, ReaderOptions } from '../types';
import Reader from './reader';
export default class ReaderSync extends Reader {
protected _walkSync: typeof fsWalk.walkSync = fsWalk.walkSync;
protected _statSync: typeof fsStat.statSync = fsStat.statSync;
public dynamic(root: string, options: ReaderOptions): Entry[] {
return this._walkSync(root, options);
}
public static(patterns: Pattern[], options: ReaderOptions): Entry[] {
const entries: Entry[] = [];
for (const pattern of patterns) {
const filepath = this._getFullEntryPath(pattern);
const entry = this._getEntry(filepath, pattern, options);
if (entry === null || !options.entryFilter(entry)) {
continue;
}
entries.push(entry);
}
return entries;
}
private _getEntry(filepath: string, pattern: Pattern, options: ReaderOptions): Entry | null {
try {
const stats = this._getStat(filepath);
return this._makeEntry(stats, pattern);
} catch (error) {
if (options.errorFilter(error as ErrnoException)) {
return null;
}
throw error;
}
}
private _getStat(filepath: string): fs.Stats {
return this._statSync(filepath, this._fsStatSettings);
}
}
fast-glob-3.2.12/src/settings.spec.ts 0000664 0000000 0000000 00000003655 14306550000 0017407 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as os from 'os';
import Settings, { DEFAULT_FILE_SYSTEM_ADAPTER } from './settings';
describe('Settings', () => {
it('should return instance with default values', () => {
const settings = new Settings();
assert.deepStrictEqual(settings.fs, DEFAULT_FILE_SYSTEM_ADAPTER);
assert.deepStrictEqual(settings.ignore, []);
assert.ok(!settings.absolute);
assert.ok(!settings.baseNameMatch);
assert.ok(!settings.dot);
assert.ok(!settings.markDirectories);
assert.ok(!settings.objectMode);
assert.ok(!settings.onlyDirectories);
assert.ok(!settings.stats);
assert.ok(!settings.suppressErrors);
assert.ok(!settings.throwErrorOnBrokenSymbolicLink);
assert.ok(settings.braceExpansion);
assert.ok(settings.caseSensitiveMatch);
assert.ok(settings.deep);
assert.ok(settings.extglob);
assert.ok(settings.followSymbolicLinks);
assert.ok(settings.globstar);
assert.ok(settings.onlyFiles);
assert.ok(settings.unique);
assert.strictEqual(settings.concurrency, os.cpus().length);
assert.strictEqual(settings.cwd, process.cwd());
});
it('should return instance with custom values', () => {
const settings = new Settings({
onlyFiles: false
});
assert.ok(!settings.onlyFiles);
});
it('should set the "onlyFiles" option when the "onlyDirectories" is enabled', () => {
const settings = new Settings({
onlyDirectories: true
});
assert.ok(!settings.onlyFiles);
assert.ok(settings.onlyDirectories);
});
it('should set the "objectMode" option when the "stats" is enabled', () => {
const settings = new Settings({
stats: true
});
assert.ok(settings.objectMode);
assert.ok(settings.stats);
});
it('should return the `fs` option with custom method', () => {
const customReaddirSync = (): never[] => [];
const settings = new Settings({
fs: { readdirSync: customReaddirSync }
});
assert.strictEqual(settings.fs.readdirSync, customReaddirSync);
});
});
fast-glob-3.2.12/src/settings.ts 0000664 0000000 0000000 00000013034 14306550000 0016446 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import * as os from 'os';
import { FileSystemAdapter, Pattern } from './types';
/**
* The `os.cpus` method can return zero. We expect the number of cores to be greater than zero.
* https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107
*/
const CPU_COUNT = Math.max(os.cpus().length, 1);
export const DEFAULT_FILE_SYSTEM_ADAPTER: FileSystemAdapter = {
lstat: fs.lstat,
lstatSync: fs.lstatSync,
stat: fs.stat,
statSync: fs.statSync,
readdir: fs.readdir,
readdirSync: fs.readdirSync
};
export type Options = {
/**
* Return the absolute path for entries.
*
* @default false
*/
absolute?: boolean;
/**
* If set to `true`, then patterns without slashes will be matched against
* the basename of the path if it contains slashes.
*
* @default false
*/
baseNameMatch?: boolean;
/**
* Enables Bash-like brace expansion.
*
* @default true
*/
braceExpansion?: boolean;
/**
* Enables a case-sensitive mode for matching files.
*
* @default true
*/
caseSensitiveMatch?: boolean;
/**
* Specifies the maximum number of concurrent requests from a reader to read
* directories.
*
* @default os.cpus().length
*/
concurrency?: number;
/**
* The current working directory in which to search.
*
* @default process.cwd()
*/
cwd?: string;
/**
* Specifies the maximum depth of a read directory relative to the start
* directory.
*
* @default Infinity
*/
deep?: number;
/**
* Allow patterns to match entries that begin with a period (`.`).
*
* @default false
*/
dot?: boolean;
/**
* Enables Bash-like `extglob` functionality.
*
* @default true
*/
extglob?: boolean;
/**
* Indicates whether to traverse descendants of symbolic link directories.
*
* @default true
*/
followSymbolicLinks?: boolean;
/**
* Custom implementation of methods for working with the file system.
*
* @default fs.*
*/
fs?: Partial;
/**
* Enables recursively repeats a pattern containing `**`.
* If `false`, `**` behaves exactly like `*`.
*
* @default true
*/
globstar?: boolean;
/**
* An array of glob patterns to exclude matches.
* This is an alternative way to use negative patterns.
*
* @default []
*/
ignore?: Pattern[];
/**
* Mark the directory path with the final slash.
*
* @default false
*/
markDirectories?: boolean;
/**
* Returns objects (instead of strings) describing entries.
*
* @default false
*/
objectMode?: boolean;
/**
* Return only directories.
*
* @default false
*/
onlyDirectories?: boolean;
/**
* Return only files.
*
* @default true
*/
onlyFiles?: boolean;
/**
* Enables an object mode (`objectMode`) with an additional `stats` field.
*
* @default false
*/
stats?: boolean;
/**
* By default this package suppress only `ENOENT` errors.
* Set to `true` to suppress any error.
*
* @default false
*/
suppressErrors?: boolean;
/**
* Throw an error when symbolic link is broken if `true` or safely
* return `lstat` call if `false`.
*
* @default false
*/
throwErrorOnBrokenSymbolicLink?: boolean;
/**
* Ensures that the returned entries are unique.
*
* @default true
*/
unique?: boolean;
};
export default class Settings {
public readonly absolute: boolean = this._getValue(this._options.absolute, false);
public readonly baseNameMatch: boolean = this._getValue(this._options.baseNameMatch, false);
public readonly braceExpansion: boolean = this._getValue(this._options.braceExpansion, true);
public readonly caseSensitiveMatch: boolean = this._getValue(this._options.caseSensitiveMatch, true);
public readonly concurrency: number = this._getValue(this._options.concurrency, CPU_COUNT);
public readonly cwd: string = this._getValue(this._options.cwd, process.cwd());
public readonly deep: number = this._getValue(this._options.deep, Infinity);
public readonly dot: boolean = this._getValue(this._options.dot, false);
public readonly extglob: boolean = this._getValue(this._options.extglob, true);
public readonly followSymbolicLinks: boolean = this._getValue(this._options.followSymbolicLinks, true);
public readonly fs: FileSystemAdapter = this._getFileSystemMethods(this._options.fs);
public readonly globstar: boolean = this._getValue(this._options.globstar, true);
public readonly ignore: Pattern[] = this._getValue(this._options.ignore, [] as Pattern[]);
public readonly markDirectories: boolean = this._getValue(this._options.markDirectories, false);
public readonly objectMode: boolean = this._getValue(this._options.objectMode, false);
public readonly onlyDirectories: boolean = this._getValue(this._options.onlyDirectories, false);
public readonly onlyFiles: boolean = this._getValue(this._options.onlyFiles, true);
public readonly stats: boolean = this._getValue(this._options.stats, false);
public readonly suppressErrors: boolean = this._getValue(this._options.suppressErrors, false);
public readonly throwErrorOnBrokenSymbolicLink: boolean = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false);
public readonly unique: boolean = this._getValue(this._options.unique, true);
constructor(private readonly _options: Options = {}) {
if (this.onlyDirectories) {
this.onlyFiles = false;
}
if (this.stats) {
this.objectMode = true;
}
}
private _getValue(option: T | undefined, value: T): T {
return option === undefined ? value : option;
}
private _getFileSystemMethods(methods: Partial = {}): FileSystemAdapter {
return {
...DEFAULT_FILE_SYSTEM_ADAPTER,
...methods
};
}
}
fast-glob-3.2.12/src/tests/ 0000775 0000000 0000000 00000000000 14306550000 0015377 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/tests/index.ts 0000664 0000000 0000000 00000000410 14306550000 0017051 0 ustar 00root root 0000000 0000000 import * as entry from './utils/entry';
import * as errno from './utils/errno';
import * as pattern from './utils/pattern';
import * as platform from './utils/platform';
import * as task from './utils/task';
export {
entry,
errno,
pattern,
platform,
task
};
fast-glob-3.2.12/src/tests/smoke/ 0000775 0000000 0000000 00000000000 14306550000 0016515 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/tests/smoke/absolute.smoke.ts 0000664 0000000 0000000 00000004532 14306550000 0022024 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as smoke from './smoke';
const CWD = process.cwd().replace(/\\/g, '/');
smoke.suite('Smoke → Absolute', [
{
pattern: 'fixtures/*',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: 'fixtures/**',
globOptions: { absolute: true },
fgOptions: { absolute: true },
broken: true,
issue: 47
},
{
pattern: 'fixtures/**/*',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: 'fixtures/../*',
globOptions: { absolute: true },
fgOptions: { absolute: true }
}
]);
smoke.suite('Smoke → Absolute (ignore)', [
{
pattern: 'fixtures/*/*',
ignore: 'fixtures/*/nested',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: 'fixtures/*/*',
ignore: '**/nested',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: 'fixtures/*',
ignore: path.posix.join(CWD, 'fixtures', '*'),
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: 'fixtures/**',
ignore: path.posix.join(CWD, 'fixtures', '*'),
globOptions: { absolute: true },
fgOptions: { absolute: true },
broken: true,
issue: 47
}
]);
smoke.suite('Smoke → Absolute (cwd)', [
{
pattern: '*',
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '**',
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '**/*',
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
}
]);
smoke.suite('Smoke → Absolute (cwd & ignore)', [
{
pattern: '*/*',
ignore: '*/nested',
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '*/*',
ignore: '**/nested',
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '*',
ignore: path.posix.join(CWD, 'fixtures', '*'),
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '**',
ignore: path.posix.join(CWD, 'fixtures', '*'),
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
},
{
pattern: '**',
ignore: path.posix.join(CWD, 'fixtures', '**'),
cwd: 'fixtures',
globOptions: { absolute: true },
fgOptions: { absolute: true }
}
]);
fast-glob-3.2.12/src/tests/smoke/base-name-match.ts 0000664 0000000 0000000 00000000301 14306550000 0022001 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → MatchBase', [
{
pattern: '*.md',
cwd: 'fixtures',
globOptions: { matchBase: true },
fgOptions: { baseNameMatch: true }
}
]);
fast-glob-3.2.12/src/tests/smoke/case-sensitive-match.smoke.ts 0000664 0000000 0000000 00000000717 14306550000 0024223 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
import * as utils from '..';
smoke.suite('Smoke → CaseSensitiveMatch', [
{
pattern: 'fixtures/File.md'
},
{
pattern: 'fixtures/File.md',
globOptions: { nocase: true },
fgOptions: { caseSensitiveMatch: false }
},
// ISSUE-276
{
pattern: '/tmp/*',
globOptions: { nocase: true, nodir: false },
fgOptions: { caseSensitiveMatch: false, onlyFiles: false },
condition: () => !utils.platform.isWindows()
}
]);
fast-glob-3.2.12/src/tests/smoke/deep.smoke.ts 0000664 0000000 0000000 00000001203 14306550000 0021113 0 ustar 00root root 0000000 0000000 /**
* @fileoverview
* The `glob` package has no `deep` option.
* So emulate it with negative patterns.
*/
import * as smoke from './smoke';
smoke.suite('Smoke → Deep', [
{
pattern: 'fixtures/**',
globOptions: { ignore: ['!fixtures/*'] },
fgOptions: { deep: 0 }
},
{
pattern: 'fixtures/**',
globOptions: { ignore: ['!fixtures/{*/,}*'] },
fgOptions: { deep: 2 }
}
]);
smoke.suite('Smoke → Deep (cwd)', [
{
pattern: '**',
cwd: 'fixtures',
globOptions: { ignore: ['!*'] },
fgOptions: { deep: 0 }
},
{
pattern: '**',
cwd: 'fixtures',
globOptions: { ignore: ['!{*/,}*'] },
fgOptions: { deep: 2 }
}
]);
fast-glob-3.2.12/src/tests/smoke/directories.smoke.ts 0000664 0000000 0000000 00000004114 14306550000 0022516 0 ustar 00root root 0000000 0000000 import * as fsUtils from '../utils/fs';
import * as smoke from './smoke';
/**
* Here we expect only directories.
* Also we skip the root directory due to ISSUE-47.
*/
function globFilter(entry: string, filepath: string): boolean {
const isRootEntry = entry === 'fixtures' || entry === 'fixtures/';
return fsUtils.isDirectory(filepath) && !isRootEntry;
}
/**
* The 'glob' package automatically adds a trailing slash when the pattern ends with it.
* We do not support this behavior and do not want to support it in the future because it is implicit behavior.
* We need real use cases if someone needs it.
*/
function globTransform(entry: string): string {
return entry.endsWith('/') ? entry.slice(0, -1) : entry;
}
smoke.suite('Smoke → Directories', [
{
pattern: 'fixtures/*',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: 'fixtures/**',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: 'fixtures/**/*',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: 'fixtures/*/',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: 'fixtures/**/',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: 'fixtures/**/*/',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
}
]);
smoke.suite('Smoke → Directories (cwd)', [
{
pattern: '*',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: '**',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: '**/*',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: '*/',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: '**/',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
},
{
pattern: '**/*/',
cwd: 'fixtures',
fgOptions: { onlyDirectories: true },
globFilter,
globTransform
}
]);
fast-glob-3.2.12/src/tests/smoke/dot.smoke.ts 0000664 0000000 0000000 00000001136 14306550000 0020771 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Dot', [
{
pattern: 'fixtures/*',
globOptions: { dot: true },
fgOptions: { dot: true }
},
{
pattern: 'fixtures/**',
globOptions: { dot: true },
fgOptions: { dot: true },
broken: true,
issue: 47
},
{
pattern: 'fixtures/**/*',
globOptions: { dot: true },
fgOptions: { dot: true }
},
{ pattern: 'fixtures/{.,}*' },
{ pattern: 'fixtures/{.*,*}' },
{ pattern: 'fixtures/**/{.,}*' },
{
pattern: 'fixtures/{.**,**}',
broken: true,
issue: 47
},
{
pattern: 'fixtures/{**/.*,**}',
broken: true,
issue: 47
}
]);
fast-glob-3.2.12/src/tests/smoke/errors.smoke.ts 0000664 0000000 0000000 00000000373 14306550000 0021521 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Errors', [
{
pattern: 'non-exist-directory/**'
},
{
pattern: 'non-exist-file.txt'
}
]);
smoke.suite('Smoke → Errors (cwd)', [
{
pattern: '**',
cwd: 'non-exist-directory'
}
]);
fast-glob-3.2.12/src/tests/smoke/files.smoke.ts 0000664 0000000 0000000 00000001336 14306550000 0021307 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Files', [
{
pattern: 'fixtures/*',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
},
{
pattern: 'fixtures/**',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
},
{
pattern: 'fixtures/**/*',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
}
]);
smoke.suite('Smoke → Files (cwd)', [
{
pattern: '*',
cwd: 'fixtures',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
},
{
pattern: '**',
cwd: 'fixtures',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
},
{
pattern: '**/*',
cwd: 'fixtures',
globOptions: { nodir: true },
fgOptions: { onlyFiles: true }
}
]);
fast-glob-3.2.12/src/tests/smoke/ignore.smoke.ts 0000664 0000000 0000000 00000000451 14306550000 0021465 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Ignore', [
{
pattern: 'fixtures/**/*',
globOptions: { ignore: ['**/*.md'] },
fgOptions: { ignore: ['**/*.md'] }
},
{
pattern: 'fixtures/**/*',
globOptions: { ignore: ['**/*.md'] },
fgOptions: { ignore: ['!**/*.md'] }
}
]);
fast-glob-3.2.12/src/tests/smoke/mark-directories.smoke.ts 0000664 0000000 0000000 00000000272 14306550000 0023447 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → MarkDirectories', [
{
pattern: 'fixtures/**/*',
globOptions: { mark: true },
fgOptions: { markDirectories: true }
}
]);
fast-glob-3.2.12/src/tests/smoke/regular.smoke.ts 0000664 0000000 0000000 00000053526 14306550000 0021656 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Regular', [
{ pattern: 'fixtures/*' },
{ pattern: 'fixtures/**', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*' },
{ pattern: 'fixtures/*/nested' },
{ pattern: 'fixtures/*/nested/*' },
{ pattern: 'fixtures/*/nested/**' },
{ pattern: 'fixtures/*/nested/**/*' },
{ pattern: 'fixtures/**/nested/*' },
{ pattern: 'fixtures/**/nested/**' },
{ pattern: 'fixtures/**/nested/**/*' },
{ pattern: 'fixtures/{first,second}' },
{ pattern: 'fixtures/{first,second}/*' },
{ pattern: 'fixtures/{first,second}/**' },
{ pattern: 'fixtures/{first,second}/**/*' },
{ pattern: '@(fixtures)/{first,second}' },
{ pattern: '@(fixtures)/{first,second}/*' },
{ pattern: 'fixtures/*/{first,second}/*' },
{ pattern: 'fixtures/*/{first,second}/*/{nested,file.md}' },
{ pattern: 'fixtures/**/{first,second}/**' },
{ pattern: 'fixtures/**/{first,second}/{nested,file.md}' },
{ pattern: 'fixtures/**/{first,second}/**/{nested,file.md}' },
{ pattern: 'fixtures/{first,second}/{nested,file.md}' },
{ pattern: 'fixtures/{first,second}/*/nested/*' },
{ pattern: 'fixtures/{first,second}/**/nested/**' },
{ pattern: 'fixtures/*/{nested,file.md}/*' },
{ pattern: 'fixtures/**/{nested,file.md}/*' },
{ pattern: './fixtures/*' }
]);
smoke.suite('Smoke → Regular (cwd)', [
{ pattern: '*', cwd: 'fixtures' },
{ pattern: '**', cwd: 'fixtures' },
{ pattern: '**/*', cwd: 'fixtures' },
{ pattern: '*/nested', cwd: 'fixtures' },
{ pattern: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', cwd: 'fixtures' },
{ pattern: '{first,second}', cwd: 'fixtures' },
{ pattern: '{first,second}/*', cwd: 'fixtures' },
{ pattern: '{first,second}/**', cwd: 'fixtures' },
{ pattern: '{first,second}/**/*', cwd: 'fixtures' },
{ pattern: '*/{first,second}/*', cwd: 'fixtures' },
{ pattern: '*/{first,second}/*/{nested,file.md}', cwd: 'fixtures' },
{ pattern: '**/{first,second}/**', cwd: 'fixtures' },
{ pattern: '**/{first,second}/{nested,file.md}', cwd: 'fixtures' },
{ pattern: '**/{first,second}/**/{nested,file.md}', cwd: 'fixtures' },
{ pattern: '{first,second}/{nested,file.md}', cwd: 'fixtures' },
{ pattern: '{first,second}/*/nested/*', cwd: 'fixtures' },
{ pattern: '{first,second}/**/nested/**', cwd: 'fixtures' },
{ pattern: '*/{nested,file.md}/*', cwd: 'fixtures' },
{ pattern: '**/{nested,file.md}/*', cwd: 'fixtures' },
{
pattern: './*',
cwd: 'fixtures',
correct: true,
reason: 'The `node-glob package returns entries with leading `./`'
}
]);
smoke.suite('Smoke → Regular (ignore)', [
[
{ pattern: 'fixtures/*', ignore: '*' },
{ pattern: 'fixtures/*', ignore: '**' },
{ pattern: 'fixtures/*', ignore: '**/*' },
{ pattern: 'fixtures/**', ignore: '*' },
{ pattern: 'fixtures/**', ignore: '**' },
{ pattern: 'fixtures/**', ignore: '**/*' },
{ pattern: 'fixtures/**/*', ignore: '*' },
{ pattern: 'fixtures/**/*', ignore: '**' },
{ pattern: 'fixtures/**/*', ignore: '**/*' }
],
[
{ pattern: 'fixtures/*', ignore: 'fixtures/*' },
{ pattern: 'fixtures/*', ignore: 'fixtures/**' },
{ pattern: 'fixtures/*', ignore: 'fixtures/**/*' },
{ pattern: 'fixtures/**', ignore: 'fixtures/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**', ignore: 'fixtures/**' },
{ pattern: 'fixtures/**', ignore: 'fixtures/**/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*', ignore: 'fixtures/*' },
{ pattern: 'fixtures/**/*', ignore: 'fixtures/**' },
{ pattern: 'fixtures/**/*', ignore: 'fixtures/**/*' }
],
[
{ pattern: 'fixtures/*', ignore: 'nested' },
{ pattern: 'fixtures/**', ignore: 'nested', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*', ignore: 'nested' }
],
[
{ pattern: 'fixtures/*', ignore: '*/nested' },
{ pattern: 'fixtures/*', ignore: '**/nested' },
{ pattern: 'fixtures/**', ignore: '*/nested', broken: true, issue: 47 },
{
pattern: 'fixtures/**',
ignore: '**/nested',
broken: true,
issue: 47,
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
},
{ pattern: 'fixtures/**/*', ignore: '*/nested' },
{
pattern: 'fixtures/**/*',
ignore: '**/nested',
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
}
],
[
{ pattern: 'fixtures/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/*', ignore: '**/nested/*' },
{ pattern: 'fixtures/**', ignore: '*/nested/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**', ignore: '**/nested/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/**/*', ignore: '**/nested/*' }
],
[
{ pattern: 'fixtures/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/*', ignore: '**/nested/**' },
{ pattern: 'fixtures/**', ignore: '*/nested/**', broken: true, issue: 47 },
{ pattern: 'fixtures/**', ignore: '**/nested/**', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/**/*', ignore: '**/nested/**' }
],
[
{ pattern: 'fixtures/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/*', ignore: '**/nested/**/*' },
{ pattern: 'fixtures/**', ignore: '*/nested/**/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**', ignore: '**/nested/**/*', broken: true, issue: 47 },
{ pattern: 'fixtures/**/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/**/*', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/*/nested', ignore: '*' },
{ pattern: 'fixtures/*/nested', ignore: '**' },
{ pattern: 'fixtures/*/nested', ignore: '**/*' },
{ pattern: 'fixtures/*/nested', ignore: 'nested' },
{ pattern: 'fixtures/*/nested', ignore: 'nested/*' },
{ pattern: 'fixtures/*/nested', ignore: 'nested/**' },
{ pattern: 'fixtures/*/nested', ignore: 'nested/**/*' },
{ pattern: 'fixtures/*/nested', ignore: '*/nested/*' },
{ pattern: 'fixtures/*/nested', ignore: '*/nested/**' },
{ pattern: 'fixtures/*/nested', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/*/nested', ignore: '**/nested/*' },
{ pattern: 'fixtures/*/nested', ignore: '**/nested/**' },
{ pattern: 'fixtures/*/nested', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/*/nested/*', ignore: '*' },
{ pattern: 'fixtures/*/nested/*', ignore: '**' },
{ pattern: 'fixtures/*/nested/*', ignore: '**/*' },
{ pattern: 'fixtures/*/nested/*', ignore: 'nested' },
{ pattern: 'fixtures/*/nested/*', ignore: 'nested/*' },
{ pattern: 'fixtures/*/nested/*', ignore: 'nested/**' },
{ pattern: 'fixtures/*/nested/*', ignore: 'nested/**/*' },
{ pattern: 'fixtures/*/nested/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/*/nested/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/*/nested/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/*/nested/*', ignore: '**/nested/*' },
{ pattern: 'fixtures/*/nested/*', ignore: '**/nested/**' },
{ pattern: 'fixtures/*/nested/*', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/*/nested/**', ignore: '*' },
{ pattern: 'fixtures/*/nested/**', ignore: '**' },
{ pattern: 'fixtures/*/nested/**', ignore: '**/*' },
{ pattern: 'fixtures/*/nested/**', ignore: 'nested' },
{ pattern: 'fixtures/*/nested/**', ignore: 'nested/*' },
{ pattern: 'fixtures/*/nested/**', ignore: 'nested/**' },
{ pattern: 'fixtures/*/nested/**', ignore: 'nested/**/*' },
{ pattern: 'fixtures/*/nested/**', ignore: '*/nested/*' },
{ pattern: 'fixtures/*/nested/**', ignore: '*/nested/**' },
{ pattern: 'fixtures/*/nested/**', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/*/nested/**', ignore: '**/nested/*' },
{ pattern: 'fixtures/*/nested/**', ignore: '**/nested/**' },
{ pattern: 'fixtures/*/nested/**', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/*/nested/**/*', ignore: '*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '**' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '**/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: 'nested' },
{ pattern: 'fixtures/*/nested/**/*', ignore: 'nested/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: 'nested/**' },
{ pattern: 'fixtures/*/nested/**/*', ignore: 'nested/**/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '**/nested/*' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '**/nested/**' },
{ pattern: 'fixtures/*/nested/**/*', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/**/nested/*', ignore: '*' },
{ pattern: 'fixtures/**/nested/*', ignore: '**' },
{ pattern: 'fixtures/**/nested/*', ignore: '**/*' },
{ pattern: 'fixtures/**/nested/*', ignore: 'nested' },
{ pattern: 'fixtures/**/nested/*', ignore: 'nested/*' },
{ pattern: 'fixtures/**/nested/*', ignore: 'nested/**' },
{ pattern: 'fixtures/**/nested/*', ignore: 'nested/**/*' },
{ pattern: 'fixtures/**/nested/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/**/nested/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/**/nested/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/**/nested/*', ignore: '**/nested/*' },
{ pattern: 'fixtures/**/nested/*', ignore: '**/nested/**' },
{ pattern: 'fixtures/**/nested/*', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/**/nested/**', ignore: '*' },
{ pattern: 'fixtures/**/nested/**', ignore: '**' },
{ pattern: 'fixtures/**/nested/**', ignore: '**/*' },
{ pattern: 'fixtures/**/nested/**', ignore: 'nested' },
{ pattern: 'fixtures/**/nested/**', ignore: 'nested/*' },
{ pattern: 'fixtures/**/nested/**', ignore: 'nested/**' },
{ pattern: 'fixtures/**/nested/**', ignore: 'nested/**/*' },
{ pattern: 'fixtures/**/nested/**', ignore: '*/nested/*' },
{ pattern: 'fixtures/**/nested/**', ignore: '*/nested/**' },
{ pattern: 'fixtures/**/nested/**', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/**/nested/**', ignore: '**/nested/*' },
{ pattern: 'fixtures/**/nested/**', ignore: '**/nested/**' },
{ pattern: 'fixtures/**/nested/**', ignore: '**/nested/**/*' }
],
[
{ pattern: 'fixtures/**/nested/**/*', ignore: '*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '**' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '**/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: 'nested' },
{ pattern: 'fixtures/**/nested/**/*', ignore: 'nested/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: 'nested/**' },
{ pattern: 'fixtures/**/nested/**/*', ignore: 'nested/**/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '*/nested/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '*/nested/**' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '*/nested/**/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '**/nested/*' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '**/nested/**' },
{ pattern: 'fixtures/**/nested/**/*', ignore: '**/nested/**/*' }
]
]);
smoke.suite('Smoke → Regular (ignore & cwd)', [
[
{ pattern: '*', ignore: '*', cwd: 'fixtures' },
{ pattern: '*', ignore: '**', cwd: 'fixtures' },
{ pattern: '*', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '**', ignore: '*', cwd: 'fixtures' },
{ pattern: '**', ignore: '**', cwd: 'fixtures' },
{ pattern: '**', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '**', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '**/*', cwd: 'fixtures' }
],
[
{ pattern: '*', ignore: 'fixtures/*', cwd: 'fixtures' },
{ pattern: '*', ignore: 'fixtures/**', cwd: 'fixtures' },
{ pattern: '*', ignore: 'fixtures/**/*', cwd: 'fixtures' },
{ pattern: '**', ignore: 'fixtures/*', cwd: 'fixtures' },
{ pattern: '**', ignore: 'fixtures/**', cwd: 'fixtures' },
{ pattern: '**', ignore: 'fixtures/**/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: 'fixtures/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: 'fixtures/**', cwd: 'fixtures' },
{ pattern: '**/*', ignore: 'fixtures/**/*', cwd: 'fixtures' }
],
[
{ pattern: '*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**/*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**/*', ignore: 'nested', cwd: 'fixtures' }
],
[
{ pattern: '*', ignore: '*/nested', cwd: 'fixtures' },
{ pattern: '*', ignore: '**/nested', cwd: 'fixtures' },
{
pattern: '**',
ignore: '*/nested',
cwd: 'fixtures',
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
},
{
pattern: '**',
ignore: '**/nested',
cwd: 'fixtures',
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
},
{
pattern: '**/*',
ignore: '*/nested',
cwd: 'fixtures',
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
},
{
pattern: '**/*',
ignore: '**/nested',
cwd: 'fixtures',
correct: true,
reason: 'The negative pattern excludes any entries (files and directories) at any nesting level.'
}
],
[
{ pattern: '*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '**', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '**/nested/*', cwd: 'fixtures' }
],
[
{ pattern: '*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '**', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '**/nested/**', cwd: 'fixtures' }
],
[
{ pattern: '*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '*', ignore: '**/nested/**/*', cwd: 'fixtures' },
{ pattern: '**', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**', ignore: '**/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/*', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '*/nested', ignore: '*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '**', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '*/nested/*', ignore: '*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '**', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/*', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '*/nested/**', ignore: '*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '**', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '*/nested/**/*', ignore: '*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '**', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '*/nested/**/*', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '**/nested/*', ignore: '*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '**', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/*', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '**/nested/**', ignore: '*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '**', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**', ignore: '**/nested/**/*', cwd: 'fixtures' }
],
[
{ pattern: '**/nested/**/*', ignore: '*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '**', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: 'nested', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: 'nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: 'nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: 'nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '*/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '*/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '*/nested/**/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '**/nested/*', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '**/nested/**', cwd: 'fixtures' },
{ pattern: '**/nested/**/*', ignore: '**/nested/**/*', cwd: 'fixtures' }
]
]);
smoke.suite('Smoke → Regular (relative)', [
{ pattern: '../*', cwd: 'fixtures/first' },
{ pattern: '../**', cwd: 'fixtures/first', broken: true, issue: 47 },
{ pattern: '../../*', cwd: 'fixtures/first/nested' },
{ pattern: '../{first,second}', cwd: 'fixtures/first' },
{ pattern: './../*', cwd: 'fixtures/first' }
]);
smoke.suite('Smoke → Regular (relative & ignore)', [
{
pattern: './../*',
cwd: 'fixtures/first',
ignore: '../*',
correct: true,
reason: 'The `node-glob` package does not exclude files, although the `../*` pattern can be applied here.'
},
{ pattern: './../*', cwd: 'fixtures/first', ignore: './../*' },
{ pattern: './../*', cwd: 'fixtures/first', ignore: '**' },
{ pattern: '../*', cwd: 'fixtures/first', ignore: '../*' },
{ pattern: '../*', cwd: 'fixtures/first', ignore: '**' },
{ pattern: '../../*', cwd: 'fixtures/first/nested', ignore: '../../*' },
{ pattern: '../../*', cwd: 'fixtures/first/nested', ignore: '**' },
{ pattern: '../{first,second}', cwd: 'fixtures/first', ignore: '../first/**' },
{ pattern: '../{first,second}', cwd: 'fixtures/first', ignore: '**/first/**' }
]);
smoke.suite('Smoke -> Regular (negative group)', [
{
pattern: '**/!(*.md)',
cwd: 'fixtures/first'
}
]);
smoke.suite('Smoke -> Regular (segmented lists)', [
{
pattern: '{book.xml,**/library/*/book.md}',
cwd: 'fixtures/third',
broken: true,
issue: 365
},
{
pattern: '{book.xml,library/**/a/book.md}',
cwd: 'fixtures/third'
}
]);
fast-glob-3.2.12/src/tests/smoke/root.smoke.ts 0000664 0000000 0000000 00000002274 14306550000 0021172 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as smoke from './smoke';
import * as utils from '..';
const CWD = process.cwd().replace(/\\/g, '/');
const ROOT = path.parse(CWD).root;
smoke.suite('Smoke → Root', [
{
pattern: '/*',
condition: () => !utils.platform.isWindows()
},
{
pattern: '/tmp/*',
condition: () => !utils.platform.isWindows()
},
{
pattern: '/*',
condition: () => utils.platform.isSupportReaddirWithFileTypes() && utils.platform.isWindows(),
correct: true,
reason: 'The `node-glob` packages returns items with resolve path for the current disk letter'
},
// UNC pattern without dynamic sections in the base section
{
pattern: `//?/${ROOT}*`,
condition: () => utils.platform.isSupportReaddirWithFileTypes() && utils.platform.isWindows(),
correct: true,
reason: 'The `node-glob` package does not allow to use UNC in patterns'
}
]);
smoke.suite('Smoke → Root (cwd)', [
{
pattern: '*',
cwd: ROOT,
condition: () => !utils.platform.isWindows() || utils.platform.isSupportReaddirWithFileTypes()
},
// UNC on Windows
{
pattern: '*',
cwd: `//?/${ROOT}`,
condition: () => utils.platform.isSupportReaddirWithFileTypes() && utils.platform.isWindows()
}
]);
fast-glob-3.2.12/src/tests/smoke/smoke.ts 0000664 0000000 0000000 00000013112 14306550000 0020201 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as path from 'path';
import * as glob from 'glob';
import * as fg from '../..';
import { Options } from '../../settings';
import { Pattern } from '../../types';
import Table = require('easy-table'); // eslint-disable-line @typescript-eslint/no-require-imports
export type SmokeTest = {
pattern: Pattern;
ignore?: Pattern;
cwd?: string;
globOptions?: glob.IOptions;
globFilter?: (entry: string, filepath: string) => boolean;
globTransform?: (entry: string) => string;
fgOptions?: Options;
/**
* Allow to run only one test case with debug information.
*/
debug?: boolean;
/**
* Mark test case as broken. This is requires a issue to repair.
*/
broken?: boolean;
issue?: number | number[];
/**
* Mark test case as correct. This is requires a reason why is true.
*/
correct?: boolean;
reason?: string;
/**
* The ability to conditionally run the test.
*/
condition?: () => boolean;
};
type MochaDefinition = Mocha.TestFunction | Mocha.ExclusiveTestFunction;
type DebugCompareTestMarker = '+' | '-';
export function suite(name: string, tests: Array): void {
const testCases = getTestCases(tests);
describe(name, () => {
for (const test of testCases) {
const title = getTestCaseTitle(test);
const definition = getTestCaseMochaDefinition(test);
definition(`${title} (sync)`, () => testCaseRunner(test, getFastGlobEntriesSync));
definition(`${title} (async)`, () => testCaseRunner(test, getFastGlobEntriesAsync));
definition(`${title} (stream)`, () => testCaseRunner(test, getFastGlobEntriesStream));
}
});
}
function getTestCases(tests: Array): SmokeTest[] {
return ([] as SmokeTest[]).concat(...tests);
}
function getTestCaseTitle(test: SmokeTest): string {
let title = `pattern: '${test.pattern}'`;
if (test.ignore !== undefined) {
title += `, ignore: '${test.ignore}'`;
}
if (test.broken !== undefined) {
title += ` (broken - ${test.issue})`;
}
if (test.correct !== undefined) {
title += ' (correct)';
}
return title;
}
function getTestCaseMochaDefinition(test: SmokeTest): MochaDefinition {
if (test.debug === true) {
return it.only;
}
if (test.condition?.() === false) {
return it.skip;
}
return it;
}
async function testCaseRunner(test: SmokeTest, func: typeof getFastGlobEntriesSync | typeof getFastGlobEntriesAsync): Promise {
const expected = getNodeGlobEntries(test);
const actual = await func(test.pattern, test.ignore, test.cwd, test.fgOptions);
if (test.debug === true) {
const report = generateDebugReport(expected, actual);
console.log(report);
}
if (test.broken === true && test.issue === undefined) {
assert.fail("This test is marked as «broken», but it doesn't have a issue key.");
}
if (test.correct === true && test.reason === undefined) {
assert.fail("This test is marked as «correct», but it doesn't have a reason.");
}
const isInvertedTest = test.broken === true || test.correct === true;
const assertAction: typeof assert.deepStrictEqual = isInvertedTest ? assert.notDeepStrictEqual : assert.deepStrictEqual;
assertAction(actual, expected);
}
function generateDebugReport(expected: string[], actual: string[]): string | null {
const table = new Table();
const items = actual.length > expected.length ? actual : expected;
if (items.length === 0) {
return null;
}
for (const item of items) {
table.cell('FIXTURES', item);
table.cell('NODE_GLOB', getTestMarker(expected, item));
table.cell('FAST_GLOB', getTestMarker(actual, item));
table.newRow();
}
return table.toString();
}
function getTestMarker(items: string[], item: string): DebugCompareTestMarker {
return items.includes(item) ? '+' : '-';
}
function getNodeGlobEntries(options: SmokeTest): string[] {
const pattern = options.pattern;
const cwd = options.cwd === undefined ? process.cwd() : options.cwd;
const ignore = options.ignore === undefined ? [] : [options.ignore];
const globFilter = options.globFilter;
const globTransform = options.globTransform;
let entries = glob.sync(pattern, { cwd, ignore, ...options.globOptions });
if (globFilter !== undefined) {
entries = entries.filter((entry) => {
const filepath = path.join(cwd, entry);
return globFilter(entry, filepath);
});
}
if (globTransform !== undefined) {
entries = entries.map((entry) => globTransform(entry));
}
entries.sort((a, b) => a.localeCompare(b));
return entries;
}
function getFastGlobEntriesSync(pattern: Pattern, ignore?: Pattern, cwd?: string, options?: Options): string[] {
return fg.sync(pattern, getFastGlobOptions(ignore, cwd, options)).sort((a, b) => a.localeCompare(b));
}
function getFastGlobEntriesAsync(pattern: Pattern, ignore?: Pattern, cwd?: string, options?: Options): Promise {
return fg(pattern, getFastGlobOptions(ignore, cwd, options)).then((entries) => {
entries.sort((a, b) => a.localeCompare(b));
return entries;
});
}
function getFastGlobEntriesStream(pattern: Pattern, ignore?: Pattern, cwd?: string, options?: Options): Promise {
const entries: string[] = [];
const stream = fg.stream(pattern, getFastGlobOptions(ignore, cwd, options));
return new Promise((resolve, reject) => {
stream.on('data', (entry: string) => entries.push(entry));
stream.once('error', reject);
stream.once('end', () => {
entries.sort((a, b) => a.localeCompare(b));
resolve(entries);
});
});
}
function getFastGlobOptions(ignore?: Pattern, cwd?: string, options?: Options): Options {
return {
cwd: cwd === undefined ? process.cwd() : cwd,
ignore: ignore === undefined ? [] : [ignore],
onlyFiles: false,
...options
};
}
fast-glob-3.2.12/src/tests/smoke/static.smoke.ts 0000664 0000000 0000000 00000004226 14306550000 0021475 0 ustar 00root root 0000000 0000000 import * as smoke from './smoke';
smoke.suite('Smoke → Static', [
{ pattern: 'fixtures' },
{ pattern: 'fixtures/file.md' },
{ pattern: 'fixtures/first' }
]);
smoke.suite('Smoke → Static (cwd)', [
{ pattern: 'file.md', cwd: 'fixtures' },
{ pattern: 'first', cwd: 'fixtures' }
]);
smoke.suite('Smoke → Static (ignore)', [
// Files
[
{ pattern: 'fixtures/file.md', ignore: 'file.md' },
{ pattern: 'fixtures/file.md', ignore: '*.md' },
{ pattern: 'fixtures/file.md', ignore: '*' },
{ pattern: 'fixtures/file.md', ignore: '**' },
{ pattern: 'fixtures/file.md', ignore: '**/*' },
{ pattern: 'fixtures/file.md', ignore: 'fixtures/file.md' },
{ pattern: 'fixtures/file.md', ignore: 'fixtures/*.md' },
{ pattern: 'fixtures/file.md', ignore: 'fixtures/*' },
{ pattern: 'fixtures/file.md', ignore: 'fixtures/**' },
{ pattern: 'fixtures/file.md', ignore: 'fixtures/**/*' }
],
// Directories
[
{ pattern: 'fixtures/first', ignore: 'first' },
{ pattern: 'fixtures/first', ignore: '*' },
{ pattern: 'fixtures/first', ignore: '**' },
{ pattern: 'fixtures/first', ignore: '**/*' },
{ pattern: 'fixtures/first', ignore: 'fixtures/first' },
{ pattern: 'fixtures/first', ignore: 'fixtures/*' },
{ pattern: 'fixtures/first', ignore: 'fixtures/**' },
{ pattern: 'fixtures/first', ignore: 'fixtures/**/*' }
]
]);
smoke.suite('Smoke → Static (ignore & cwd)', [
// Files
[
{ pattern: 'fixtures/file.md', ignore: 'file.md', cwd: 'fixtures' },
{ pattern: 'fixtures/file.md', ignore: '*.md', cwd: 'fixtures' },
{ pattern: 'fixtures/file.md', ignore: '*', cwd: 'fixtures' },
{ pattern: 'fixtures/file.md', ignore: '**', cwd: 'fixtures' },
{ pattern: 'fixtures/file.md', ignore: '**/*', cwd: 'fixtures' }
],
// Directories
[
{ pattern: 'fixtures/first', ignore: 'first', cwd: 'fixtures' },
{ pattern: 'fixtures/first', ignore: '*', cwd: 'fixtures' },
{ pattern: 'fixtures/first', ignore: '**', cwd: 'fixtures' },
{ pattern: 'fixtures/first', ignore: '**/*', cwd: 'fixtures' }
]
]);
smoke.suite('Smoke → Static (relative)', [
{ pattern: '../file.md', cwd: 'fixtures/first' },
{ pattern: '../../file.md', cwd: 'fixtures/first/nested' }
]);
fast-glob-3.2.12/src/tests/utils/ 0000775 0000000 0000000 00000000000 14306550000 0016537 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/tests/utils/entry.ts 0000664 0000000 0000000 00000002156 14306550000 0020254 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import { Dirent, Stats } from '@nodelib/fs.macchiato';
import { Entry } from '../../types';
class EntryBuilder {
private _isFile: boolean = true;
private _isDirectory: boolean = false;
private _isSymbolicLink: boolean = false;
private readonly _entry: Entry = {
name: '',
path: '',
dirent: new Dirent()
};
public path(filepath: string): this {
this._entry.name = path.basename(filepath);
this._entry.path = filepath;
return this;
}
public file(): this {
this._isFile = true;
this._isDirectory = false;
return this;
}
public directory(): this {
this._isDirectory = true;
this._isFile = false;
return this;
}
public symlink(): this {
this._isSymbolicLink = true;
return this;
}
public stats(): this {
this._entry.stats = new Stats();
return this;
}
public build(): Entry {
this._entry.dirent = new Dirent({
name: this._entry.name,
isFile: this._isFile,
isDirectory: this._isDirectory,
isSymbolicLink: this._isSymbolicLink
});
return this._entry;
}
}
export function builder(): EntryBuilder {
return new EntryBuilder();
}
fast-glob-3.2.12/src/tests/utils/errno.ts 0000664 0000000 0000000 00000000711 14306550000 0020233 0 ustar 00root root 0000000 0000000 import { ErrnoException } from '../../types';
class SystemError extends Error implements ErrnoException {
constructor(public readonly code: string, message: string) {
super(`${code}: ${message}`);
this.name = 'SystemError';
}
}
export function getEnoent(): ErrnoException {
return new SystemError('ENOENT', 'no such file or directory');
}
export function getEperm(): ErrnoException {
return new SystemError('EPERM', 'operation not permitted');
}
fast-glob-3.2.12/src/tests/utils/fs.ts 0000664 0000000 0000000 00000000233 14306550000 0017515 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
export function isDirectory(filepath: string): boolean {
const stats = fs.lstatSync(filepath);
return stats.isDirectory();
}
fast-glob-3.2.12/src/tests/utils/pattern.ts 0000664 0000000 0000000 00000003161 14306550000 0020565 0 ustar 00root root 0000000 0000000 import { Pattern, MicromatchOptions } from '../../types';
import * as utils from '../../utils';
import { PatternSegment, PatternInfo } from '../../providers/matchers/matcher';
class PatternSegmentBuilder {
private readonly _segment: PatternSegment = {
dynamic: false,
pattern: ''
};
public dynamic(): this {
this._segment.dynamic = true;
return this;
}
public pattern(pattern: Pattern): this {
this._segment.pattern = pattern;
return this;
}
public build(options: MicromatchOptions = {}): PatternSegment {
if (!this._segment.dynamic) {
return this._segment;
}
return {
...this._segment,
patternRe: utils.pattern.makeRe(this._segment.pattern, options)
};
}
}
class PatternInfoBuilder {
private readonly _section: PatternInfo = {
complete: true,
pattern: '',
segments: [],
sections: []
};
public section(...segments: PatternSegment[]): this {
this._section.sections.push(segments);
if (this._section.segments.length === 0) {
this._section.complete = true;
this._section.segments.push(...segments);
} else {
this._section.complete = false;
const globstar = segment().dynamic().pattern('**').build();
this._section.segments.push(globstar, ...segments);
}
return this;
}
public build(): PatternInfo {
return {
...this._section,
pattern: this._buildPattern()
};
}
private _buildPattern(): Pattern {
return this._section.segments.map((segment) => segment.pattern).join('/');
}
}
export function segment(): PatternSegmentBuilder {
return new PatternSegmentBuilder();
}
export function info(): PatternInfoBuilder {
return new PatternInfoBuilder();
}
fast-glob-3.2.12/src/tests/utils/platform.ts 0000664 0000000 0000000 00000001507 14306550000 0020736 0 ustar 00root root 0000000 0000000 import * as os from 'os';
const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.');
const MAJOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[0], 10);
const MINOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[1], 10);
const SUPPORTED_MAJOR_VERSION = 10;
const SUPPORTED_MINOR_VERSION = 10;
const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION;
const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION;
/**
* IS `true` for Node.js 10.10 and greater.
*/
export const IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR;
export function isWindows(): boolean {
return os.platform() === 'win32';
}
export function isSupportReaddirWithFileTypes(): boolean {
return IS_SUPPORT_READDIR_WITH_FILE_TYPES;
}
fast-glob-3.2.12/src/tests/utils/task.ts 0000664 0000000 0000000 00000001411 14306550000 0020046 0 ustar 00root root 0000000 0000000 import { Task } from '../../managers/tasks';
import { Pattern } from '../../types';
class TaskBuilder {
private readonly _task: Task = {
base: '',
dynamic: true,
patterns: [],
positive: [],
negative: []
};
public base(base: string): this {
this._task.base = base;
return this;
}
public static(): this {
this._task.dynamic = false;
return this;
}
public positive(pattern: Pattern): this {
this._task.patterns.push(pattern);
this._task.positive.push(pattern);
return this;
}
public negative(pattern: Pattern): this {
this._task.patterns.push(`!${pattern}`);
this._task.negative.push(pattern);
return this;
}
public build(): Task {
return this._task;
}
}
export function builder(): TaskBuilder {
return new TaskBuilder();
}
fast-glob-3.2.12/src/types/ 0000775 0000000 0000000 00000000000 14306550000 0015401 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/types/index.ts 0000664 0000000 0000000 00000002012 14306550000 0017053 0 ustar 00root root 0000000 0000000 import * as fsWalk from '@nodelib/fs.walk';
export type ErrnoException = NodeJS.ErrnoException;
export type Entry = fsWalk.Entry;
export type EntryItem = string | Entry;
export type Pattern = string;
export type PatternRe = RegExp;
export type PatternsGroup = Record;
export type ReaderOptions = fsWalk.Options & {
transform(entry: Entry): EntryItem;
deepFilter: DeepFilterFunction;
entryFilter: EntryFilterFunction;
errorFilter: ErrorFilterFunction;
fs: FileSystemAdapter;
stats: boolean;
};
export type ErrorFilterFunction = fsWalk.ErrorFilterFunction;
export type EntryFilterFunction = fsWalk.EntryFilterFunction;
export type DeepFilterFunction = fsWalk.DeepFilterFunction;
export type EntryTransformerFunction = (entry: Entry) => EntryItem;
export type MicromatchOptions = {
dot?: boolean;
matchBase?: boolean;
nobrace?: boolean;
nocase?: boolean;
noext?: boolean;
noglobstar?: boolean;
posix?: boolean;
strictSlashes?: boolean;
};
export type FileSystemAdapter = fsWalk.FileSystemAdapter;
fast-glob-3.2.12/src/utils/ 0000775 0000000 0000000 00000000000 14306550000 0015375 5 ustar 00root root 0000000 0000000 fast-glob-3.2.12/src/utils/array.spec.ts 0000664 0000000 0000000 00000001671 14306550000 0020021 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as util from './array';
describe('Utils → Array', () => {
describe('.flatten', () => {
it('should return non-nested array', () => {
const expected = ['a', 'b'];
const actual = util.flatten([['a'], ['b']]);
assert.deepStrictEqual(actual, expected);
});
});
describe('.splitWhen', () => {
it('should return one group', () => {
const expected = [[1, 2]];
const actual = util.splitWhen([1, 2], () => false);
assert.deepStrictEqual(actual, expected);
});
it('should return group for each item of array', () => {
const expected = [[], [], [], []];
const actual = util.splitWhen([1, 2, 3], () => true);
assert.deepStrictEqual(actual, expected);
});
it('should return two group', () => {
const expected = [[1, 2], [4, 5]];
const actual = util.splitWhen([1, 2, 3, 4, 5], (item) => item === 3);
assert.deepStrictEqual(actual, expected);
});
});
});
fast-glob-3.2.12/src/utils/array.ts 0000664 0000000 0000000 00000000701 14306550000 0017061 0 ustar 00root root 0000000 0000000 export function flatten(items: T[][]): T[] {
return items.reduce((collection, item) => ([] as T[]).concat(collection, item), [] as T[]);
}
export function splitWhen(items: T[], predicate: (item: T) => boolean): T[][] {
const result: T[][] = [[]];
let groupIndex = 0;
for (const item of items) {
if (predicate(item)) {
groupIndex++;
result[groupIndex] = [];
} else {
result[groupIndex].push(item);
}
}
return result;
}
fast-glob-3.2.12/src/utils/errno.spec.ts 0000664 0000000 0000000 00000000660 14306550000 0020025 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as tests from '../tests';
import * as util from './errno';
describe('Utils → Errno', () => {
describe('.isEnoentCodeError', () => {
it('should return true for ENOENT error', () => {
assert.ok(util.isEnoentCodeError(tests.errno.getEnoent()));
});
it('should return false for EPERM error', () => {
assert.ok(!util.isEnoentCodeError(tests.errno.getEperm()));
});
});
});
fast-glob-3.2.12/src/utils/errno.ts 0000664 0000000 0000000 00000000223 14306550000 0017067 0 ustar 00root root 0000000 0000000 import { ErrnoException } from '../types';
export function isEnoentCodeError(error: ErrnoException): boolean {
return error.code === 'ENOENT';
}
fast-glob-3.2.12/src/utils/fs.spec.ts 0000664 0000000 0000000 00000001162 14306550000 0017306 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { Stats } from '@nodelib/fs.macchiato';
import * as util from './fs';
describe('Utils → FS', () => {
describe('.createDirentFromStats', () => {
it('should convert fs.Stats to fs.Dirent', () => {
const actual = util.createDirentFromStats('name', new Stats());
assert.strictEqual(actual.name, 'name');
assert.ok(!actual.isBlockDevice());
assert.ok(!actual.isCharacterDevice());
assert.ok(!actual.isDirectory());
assert.ok(!actual.isFIFO());
assert.ok(actual.isFile());
assert.ok(!actual.isSocket());
assert.ok(!actual.isSymbolicLink());
});
});
});
fast-glob-3.2.12/src/utils/fs.ts 0000664 0000000 0000000 00000001721 14306550000 0016356 0 ustar 00root root 0000000 0000000 import * as fs from 'fs';
import { Dirent } from '@nodelib/fs.walk';
class DirentFromStats implements fs.Dirent {
public isBlockDevice: fs.Stats['isBlockDevice'];
public isCharacterDevice: fs.Stats['isCharacterDevice'];
public isDirectory: fs.Stats['isDirectory'];
public isFIFO: fs.Stats['isFIFO'];
public isFile: fs.Stats['isFile'];
public isSocket: fs.Stats['isSocket'];
public isSymbolicLink: fs.Stats['isSymbolicLink'];
constructor(public name: string, stats: fs.Stats) {
this.isBlockDevice = stats.isBlockDevice.bind(stats);
this.isCharacterDevice = stats.isCharacterDevice.bind(stats);
this.isDirectory = stats.isDirectory.bind(stats);
this.isFIFO = stats.isFIFO.bind(stats);
this.isFile = stats.isFile.bind(stats);
this.isSocket = stats.isSocket.bind(stats);
this.isSymbolicLink = stats.isSymbolicLink.bind(stats);
}
}
export function createDirentFromStats(name: string, stats: fs.Stats): Dirent {
return new DirentFromStats(name, stats);
}
fast-glob-3.2.12/src/utils/index.ts 0000664 0000000 0000000 00000000462 14306550000 0017056 0 ustar 00root root 0000000 0000000 import * as array from './array';
import * as errno from './errno';
import * as fs from './fs';
import * as path from './path';
import * as pattern from './pattern';
import * as stream from './stream';
import * as string from './string';
export {
array,
errno,
fs,
path,
pattern,
stream,
string
};
fast-glob-3.2.12/src/utils/path.spec.ts 0000664 0000000 0000000 00000005454 14306550000 0017642 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as path from 'path';
import * as util from './path';
describe('Utils → Path', () => {
describe('.unixify', () => {
it('should return path with converted slashes', () => {
const expected = 'directory/nested/file.md';
const actual = util.unixify('directory\\nested/file.md');
assert.strictEqual(actual, expected);
});
});
describe('.makeAbsolute', () => {
it('should return absolute filepath', () => {
const expected = path.join(process.cwd(), 'file.md');
const actual = util.makeAbsolute(process.cwd(), 'file.md');
assert.strictEqual(actual, expected);
});
});
describe('.escapePattern', () => {
it('should return pattern with escaped glob symbols', () => {
assert.strictEqual(util.escape('!abc'), '\\!abc');
assert.strictEqual(util.escape('*'), '\\*');
assert.strictEqual(util.escape('?'), '\\?');
assert.strictEqual(util.escape('()'), '\\(\\)');
assert.strictEqual(util.escape('{}'), '\\{\\}');
assert.strictEqual(util.escape('[]'), '\\[\\]');
assert.strictEqual(util.escape('@('), '\\@\\(');
assert.strictEqual(util.escape('!('), '\\!\\(');
assert.strictEqual(util.escape('*('), '\\*\\(');
assert.strictEqual(util.escape('?('), '\\?\\(');
assert.strictEqual(util.escape('+('), '\\+\\(');
});
it('should return pattern without additional escape characters', () => {
assert.strictEqual(util.escape('\\!abc'), '\\!abc');
assert.strictEqual(util.escape('\\*'), '\\*');
assert.strictEqual(util.escape('\\!\\('), '\\!\\(');
});
it('should return pattern without escape characters', () => {
assert.strictEqual(util.escape('abc!'), 'abc!');
assert.strictEqual(util.escape('abc/!abc'), 'abc/!abc');
assert.strictEqual(util.escape('+abc'), '+abc');
assert.strictEqual(util.escape('abc+'), 'abc+');
assert.strictEqual(util.escape('@abc'), '@abc');
assert.strictEqual(util.escape('abc@'), 'abc@');
});
});
describe('.removeLeadingDotCharacters', () => {
it('should return path without changes', () => {
assert.strictEqual(util.removeLeadingDotSegment('../a/b'), '../a/b');
assert.strictEqual(util.removeLeadingDotSegment('~/a/b'), '~/a/b');
assert.strictEqual(util.removeLeadingDotSegment('/a/b'), '/a/b');
assert.strictEqual(util.removeLeadingDotSegment('a/b'), 'a/b');
assert.strictEqual(util.removeLeadingDotSegment('..\\a\\b'), '..\\a\\b');
assert.strictEqual(util.removeLeadingDotSegment('~\\a\\b'), '~\\a\\b');
assert.strictEqual(util.removeLeadingDotSegment('\\a\\b'), '\\a\\b');
assert.strictEqual(util.removeLeadingDotSegment('a\\b'), 'a\\b');
});
it('should return path without leading dit characters', () => {
assert.strictEqual(util.removeLeadingDotSegment('./a/b'), 'a/b');
assert.strictEqual(util.removeLeadingDotSegment('.\\a\\b'), 'a\\b');
});
});
});
fast-glob-3.2.12/src/utils/path.ts 0000664 0000000 0000000 00000002042 14306550000 0016677 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import { Pattern } from '../types';
const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\
const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g;
/**
* Designed to work only with simple paths: `dir\\file`.
*/
export function unixify(filepath: string): string {
return filepath.replace(/\\/g, '/');
}
export function makeAbsolute(cwd: string, filepath: string): string {
return path.resolve(cwd, filepath);
}
export function escape(pattern: Pattern): Pattern {
return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2');
}
export function removeLeadingDotSegment(entry: string): string {
// We do not use `startsWith` because this is 10x slower than current implementation for some cases.
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
if (entry.charAt(0) === '.') {
const secondCharactery = entry.charAt(1);
if (secondCharactery === '/' || secondCharactery === '\\') {
return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT);
}
}
return entry;
}
fast-glob-3.2.12/src/utils/pattern.spec.ts 0000664 0000000 0000000 00000036513 14306550000 0020363 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import { Pattern } from '../types';
import * as util from './pattern';
describe('Utils → Pattern', () => {
describe('.isStaticPattern', () => {
it('should return true for static pattern', () => {
const actual = util.isStaticPattern('dir');
assert.ok(actual);
});
it('should return false for dynamic pattern', () => {
const actual = util.isStaticPattern('*');
assert.ok(!actual);
});
});
describe('.isDynamicPattern', () => {
describe('Without options', () => {
it('should return false for an empty string', () => {
assert.ok(!util.isDynamicPattern(''));
});
it('should return true for patterns that include the escape symbol', () => {
assert.ok(util.isDynamicPattern('\\'));
});
it('should return true for everything when the `caseSensitiveMatch` option is disabled', () => {
assert.ok(util.isDynamicPattern('abc', { caseSensitiveMatch: false }));
});
it('should return true for patterns that include common glob symbols', () => {
assert.ok(util.isDynamicPattern('*'));
assert.ok(util.isDynamicPattern('abc/*'));
assert.ok(util.isDynamicPattern('?'));
assert.ok(util.isDynamicPattern('abc/?'));
assert.ok(util.isDynamicPattern('!abc'));
});
it('should return true for patterns that include regex group symbols', () => {
assert.ok(util.isDynamicPattern('(a|)'));
assert.ok(util.isDynamicPattern('(a|b)'));
assert.ok(util.isDynamicPattern('abc/(a|b)'));
});
it('should return true for patterns that include regex character class symbols', () => {
assert.ok(util.isDynamicPattern('[abc]'));
assert.ok(util.isDynamicPattern('abc/[abc]'));
assert.ok(util.isDynamicPattern('[^abc]'));
assert.ok(util.isDynamicPattern('abc/[^abc]'));
assert.ok(util.isDynamicPattern('[1-3]'));
assert.ok(util.isDynamicPattern('abc/[1-3]'));
assert.ok(util.isDynamicPattern('[[:alpha:][:digit:]]'));
assert.ok(util.isDynamicPattern('abc/[[:alpha:][:digit:]]'));
});
it('should return true for patterns that include glob extension symbols', () => {
assert.ok(util.isDynamicPattern('@()'));
assert.ok(util.isDynamicPattern('@(a)'));
assert.ok(util.isDynamicPattern('@(a|b)'));
assert.ok(util.isDynamicPattern('abc/!(a|b)'));
assert.ok(util.isDynamicPattern('*(a|b)'));
assert.ok(util.isDynamicPattern('?(a|b)'));
assert.ok(util.isDynamicPattern('+(a|b)'));
});
it('should return false for glob extension when the `extglob` option is disabled', () => {
assert.ok(!util.isDynamicPattern('@()', { extglob: false }));
assert.ok(!util.isDynamicPattern('@(a)', { extglob: false }));
assert.ok(!util.isDynamicPattern('@(a|b)', { extglob: false }));
assert.ok(!util.isDynamicPattern('abc/!(a|b)', { extglob: false }));
assert.ok(util.isDynamicPattern('*(a|b)', { extglob: true }));
assert.ok(util.isDynamicPattern('?(a|b)', { extglob: true }));
assert.ok(!util.isDynamicPattern('+(a|b)', { extglob: false }));
});
it('should return true for patterns that include brace expansions symbols', () => {
assert.ok(util.isDynamicPattern('{,}'));
assert.ok(util.isDynamicPattern('abc/{a.txt,}'));
assert.ok(util.isDynamicPattern('{a,}'));
assert.ok(util.isDynamicPattern('{,b}'));
assert.ok(util.isDynamicPattern('{a,b}'));
assert.ok(util.isDynamicPattern('{a,b,c}'));
assert.ok(util.isDynamicPattern('{a' + ','.repeat(999999) + 'b}'));
assert.ok(util.isDynamicPattern('{a,b,{c,d}}'));
// The second braces pass
assert.ok(util.isDynamicPattern('{a,b,{c,d}'));
assert.ok(util.isDynamicPattern('{1..3}'));
assert.ok(util.isDynamicPattern('abc/{1..3}'));
assert.ok(util.isDynamicPattern('{2..10..2}'));
});
it('should return false for brace extension when the `braceExpansion` option is disabled', () => {
assert.ok(!util.isDynamicPattern('{,}', { braceExpansion: false }));
assert.ok(!util.isDynamicPattern('{a,}', { braceExpansion: false }));
assert.ok(!util.isDynamicPattern('{,b}', { braceExpansion: false }));
assert.ok(!util.isDynamicPattern('{a,b}', { braceExpansion: false }));
assert.ok(!util.isDynamicPattern('{1..3}', { braceExpansion: false }));
});
it('should return false for "!" symbols when a symbol is not specified first in the string', () => {
assert.ok(!util.isDynamicPattern('abc!'));
});
it('should return false for a completely static pattern', () => {
assert.ok(!util.isDynamicPattern(''));
assert.ok(!util.isDynamicPattern('.'));
assert.ok(!util.isDynamicPattern('abc'));
assert.ok(!util.isDynamicPattern('~abc'));
assert.ok(!util.isDynamicPattern('~/abc'));
assert.ok(!util.isDynamicPattern('+~/abc'));
assert.ok(!util.isDynamicPattern('@.(abc)'));
assert.ok(!util.isDynamicPattern('(a b)'));
assert.ok(!util.isDynamicPattern('(a b)'));
assert.ok(!util.isDynamicPattern('[abc'));
});
it('should return false for unfinished regex character class', () => {
assert.ok(!util.isDynamicPattern('['));
assert.ok(!util.isDynamicPattern('['.repeat(999999)));
assert.ok(!util.isDynamicPattern('[abc'));
});
it('should return false for unfinished regex group', () => {
assert.ok(!util.isDynamicPattern('(a|b'));
assert.ok(!util.isDynamicPattern('('.repeat(999999) + 'a|b'));
assert.ok(!util.isDynamicPattern('(a' + '|'.repeat(999999) + 'b'));
assert.ok(!util.isDynamicPattern('abc/(a|b'));
});
it('should return false for unfinished glob extension', () => {
assert.ok(!util.isDynamicPattern('@('));
assert.ok(!util.isDynamicPattern('@' + '('.repeat(999999) + 'a'));
assert.ok(!util.isDynamicPattern('@(a'));
assert.ok(!util.isDynamicPattern('@(a|'));
assert.ok(!util.isDynamicPattern('@(a|b'));
});
it('should return false for unfinished brace expansions', () => {
assert.ok(!util.isDynamicPattern('{'));
assert.ok(!util.isDynamicPattern('{'.repeat(999999)));
assert.ok(!util.isDynamicPattern('{a'));
assert.ok(!util.isDynamicPattern('{a}'));
assert.ok(!util.isDynamicPattern('{,'));
assert.ok(!util.isDynamicPattern('{a,'));
assert.ok(!util.isDynamicPattern('{a,b'));
assert.ok(!util.isDynamicPattern('{a' + ','.repeat(999999) + 'b'));
assert.ok(!util.isDynamicPattern('{1..'));
assert.ok(!util.isDynamicPattern('{1.' + '.'.repeat(999999) + '2'));
assert.ok(!util.isDynamicPattern('{2..10'));
assert.ok(!util.isDynamicPattern('{2..10.'));
assert.ok(!util.isDynamicPattern('{2..10..'));
assert.ok(!util.isDynamicPattern('{2..10..2'));
});
});
describe('With options', () => {
it('should return true for patterns that include "*?" symbols even when the "extglob" option is disabled', () => {
assert.ok(util.isDynamicPattern('*(a|b)', { extglob: false }));
assert.ok(util.isDynamicPattern('?(a|b)', { extglob: false }));
});
it('should return true when the "caseSensitiveMatch" option is enabled', () => {
assert.ok(util.isDynamicPattern('a', { caseSensitiveMatch: false }));
});
it('should return false for glob extension when the "extglob" option is disabled', () => {
assert.ok(!util.isDynamicPattern('@(a|b)', { extglob: false }));
assert.ok(!util.isDynamicPattern('abc/!(a|b)', { extglob: false }));
assert.ok(!util.isDynamicPattern('+(a|b)', { extglob: false }));
});
it('should return false for brace expansions when the "braceExpansion" option is disabled', () => {
assert.ok(!util.isDynamicPattern('{a,b}', { braceExpansion: false }));
assert.ok(!util.isDynamicPattern('{1..3}', { braceExpansion: false }));
});
});
});
describe('.convertToPositivePattern', () => {
it('should returns converted positive pattern', () => {
const expected = '*.js';
const actual = util.convertToPositivePattern('!*.js');
assert.strictEqual(actual, expected);
});
it('should returns pattern without changes', () => {
const expected = '*.js';
const actual = util.convertToPositivePattern('*.js');
assert.strictEqual(actual, expected);
});
});
describe('.convertToNegativePattern', () => {
it('should returns converted negative pattern', () => {
const expected = '!*.js';
const actual = util.convertToNegativePattern('*.js');
assert.strictEqual(actual, expected);
});
});
describe('.isNegativePattern', () => {
it('should returns true', () => {
const actual = util.isNegativePattern('!*.md');
assert.ok(actual);
});
it('should returns false', () => {
const actual = util.isNegativePattern('*.md');
assert.ok(!actual);
});
it('should returns false for extglob', () => {
const actual = util.isNegativePattern('!(a|b|c)');
assert.ok(!actual);
});
});
describe('.isPositivePattern', () => {
it('should returns true', () => {
const actual = util.isPositivePattern('*.md');
assert.ok(actual);
});
it('should returns false', () => {
const actual = util.isPositivePattern('!*.md');
assert.ok(!actual);
});
});
describe('.getNegativePatterns', () => {
it('should returns only negative patterns', () => {
const expected = ['!*.spec.js'];
const actual = util.getNegativePatterns(['*.js', '!*.spec.js', '*.ts']);
assert.deepStrictEqual(actual, expected);
});
it('should returns empty array', () => {
const expected: Pattern[] = [];
const actual = util.getNegativePatterns(['*.js', '*.ts']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.getPositivePatterns', () => {
it('should returns only positive patterns', () => {
const expected = ['*.js', '*.ts'];
const actual = util.getPositivePatterns(['*.js', '!*.spec.js', '*.ts']);
assert.deepStrictEqual(actual, expected);
});
it('should returns empty array', () => {
const expected: Pattern[] = [];
const actual = util.getPositivePatterns(['!*.js', '!*.ts']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.getPatternsInsideCurrentDirectory', () => {
it('should return patterns', () => {
const expected: Pattern[] = ['.', './*', '*', 'a/*'];
const actual = util.getPatternsInsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.getPatternsOutsideCurrentDirectory', () => {
it('should return patterns', () => {
const expected: Pattern[] = ['..', '../*', './..', './../*'];
const actual = util.getPatternsOutsideCurrentDirectory(['.', './*', '*', 'a/*', '..', '../*', './..', './../*']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.isPatternRelatedToParentDirectory', () => {
it('should be `false` when the pattern refers to the current directory', () => {
const actual = util.isPatternRelatedToParentDirectory('.');
assert.ok(!actual);
});
it('should be `true` when the pattern equals to `..`', () => {
const actual = util.isPatternRelatedToParentDirectory('..');
assert.ok(actual);
});
it('should be `true` when the pattern starts with `..` segment', () => {
const actual = util.isPatternRelatedToParentDirectory('../*');
assert.ok(actual);
});
it('should be `true` when the pattern starts with `./..` segment', () => {
const actual = util.isPatternRelatedToParentDirectory('./../*');
assert.ok(actual);
});
});
describe('.getBaseDirectory', () => {
it('should returns base directory', () => {
const expected = 'root';
const actual = util.getBaseDirectory('root/*.js');
assert.strictEqual(actual, expected);
});
it('should returns base directory without slash transformation', () => {
const expected = '.';
const actual = util.getBaseDirectory('file-\\(suffix\\).md');
assert.strictEqual(actual, expected);
});
});
describe('.hasGlobStar', () => {
it('should returns true for pattern that includes globstar', () => {
const actual = util.hasGlobStar('**/*.js');
assert.ok(actual);
});
it('should returns false for pattern that has no globstar', () => {
const actual = util.hasGlobStar('*.js');
assert.ok(!actual);
});
});
describe('.endsWithSlashGlobStar', () => {
it('should returns true for pattern that ends with slash and globstar', () => {
const actual = util.endsWithSlashGlobStar('name/**');
assert.ok(actual);
});
it('should returns false for pattern that has no slash, but ends with globstar', () => {
const actual = util.endsWithSlashGlobStar('**');
assert.ok(!actual);
});
it('should returns false for pattern that does not ends with globstar', () => {
const actual = util.endsWithSlashGlobStar('name/**/*');
assert.ok(!actual);
});
});
describe('.isAffectDepthOfReadingPattern', () => {
it('should return true for pattern that ends with slash and globstar', () => {
const actual = util.isAffectDepthOfReadingPattern('name/**');
assert.ok(actual);
});
it('should return true for pattern when the last partial of the pattern is static pattern', () => {
const actual = util.isAffectDepthOfReadingPattern('**/name');
assert.ok(actual);
});
it('should return false', () => {
const actual = util.isAffectDepthOfReadingPattern('**/name/*');
assert.ok(!actual);
});
});
describe('.expandPatternsWithBraceExpansion', () => {
it('should return an array of expanded patterns with brace expansion', () => {
const expected = ['a/b/d', 'a/c/d', 'a/*', 'a/b/c'];
const actual = util.expandPatternsWithBraceExpansion(['a/{b,c}/d', 'a/{*,b/c}']);
assert.deepStrictEqual(actual, expected);
});
});
describe('.expandBraceExpansion', () => {
it('should return an array of expanded patterns with brace expansion without dupes', () => {
const expected = ['a/b', 'a/c/d', 'a/c'];
const actual = util.expandBraceExpansion('a/{b,c/d,{b,c}}');
assert.deepStrictEqual(actual, expected);
});
});
describe('.getPatternParts', () => {
it('should return an array with a single item when the pattern is an empty string', () => {
const expected: Pattern[] = [''];
const actual = util.getPatternParts('', {});
assert.deepStrictEqual(actual, expected);
});
it('should return an array with a single item (micromatch/picomatch#58)', () => {
const expected: Pattern[] = ['a*'];
const actual = util.getPatternParts('a*', {});
assert.deepStrictEqual(actual, expected);
});
it('should return the correct set of parts for the pattern with a forward slash (micromatch/picomatch#58)', () => {
const expected: Pattern[] = ['', 'lib', '*'];
const actual = util.getPatternParts('/lib/*', {});
assert.deepStrictEqual(actual, expected);
});
it('should return an array of pattern parts', () => {
const expected: Pattern[] = ['a', '*', 'b', '**', 'c'];
const actual = util.getPatternParts('a/*/b/**/c', {});
assert.deepStrictEqual(actual, expected);
});
});
describe('.makeRe', () => {
it('should return regexp for provided pattern', () => {
const actual = util.makeRe('*.js', {});
assert.ok(actual instanceof RegExp);
});
});
describe('.convertPatternsToRe', () => {
it('should return regexps for provided patterns', () => {
const [actual] = util.convertPatternsToRe(['*.js'], {});
assert.ok(actual instanceof RegExp);
});
});
describe('.matchAny', () => {
it('should return true', () => {
const actual = util.matchAny('fixtures/nested/file.txt', [/fixture/, /fixtures\/nested\/file/]);
assert.ok(actual);
});
it('should return false', () => {
const actual = util.matchAny('fixtures/directory', [/fixtures\/file/]);
assert.ok(!actual);
});
});
});
fast-glob-3.2.12/src/utils/pattern.ts 0000664 0000000 0000000 00000013037 14306550000 0017426 0 ustar 00root root 0000000 0000000 import * as path from 'path';
import * as globParent from 'glob-parent';
import * as micromatch from 'micromatch';
import { MicromatchOptions, Pattern, PatternRe } from '../types';
const GLOBSTAR = '**';
const ESCAPE_SYMBOL = '\\';
const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/;
const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[[^[]*]/;
const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\([^(]*\|[^|]*\)/;
const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\([^(]*\)/;
const BRACE_EXPANSION_SEPARATORS_RE = /,|\.\./;
type PatternTypeOptions = {
braceExpansion?: boolean;
caseSensitiveMatch?: boolean;
extglob?: boolean;
};
export function isStaticPattern(pattern: Pattern, options: PatternTypeOptions = {}): boolean {
return !isDynamicPattern(pattern, options);
}
export function isDynamicPattern(pattern: Pattern, options: PatternTypeOptions = {}): boolean {
/**
* A special case with an empty string is necessary for matching patterns that start with a forward slash.
* An empty string cannot be a dynamic pattern.
* For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'.
*/
if (pattern === '') {
return false;
}
/**
* When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check
* filepath directly (without read directory).
*/
if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) {
return true;
}
if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) {
return true;
}
if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) {
return true;
}
if (options.braceExpansion !== false && hasBraceExpansion(pattern)) {
return true;
}
return false;
}
function hasBraceExpansion(pattern: string): boolean {
const openingBraceIndex = pattern.indexOf('{');
if (openingBraceIndex === -1) {
return false;
}
const closingBraceIndex = pattern.indexOf('}', openingBraceIndex + 1);
if (closingBraceIndex === -1) {
return false;
}
const braceContent = pattern.slice(openingBraceIndex, closingBraceIndex);
return BRACE_EXPANSION_SEPARATORS_RE.test(braceContent);
}
export function convertToPositivePattern(pattern: Pattern): Pattern {
return isNegativePattern(pattern) ? pattern.slice(1) : pattern;
}
export function convertToNegativePattern(pattern: Pattern): Pattern {
return '!' + pattern;
}
export function isNegativePattern(pattern: Pattern): boolean {
return pattern.startsWith('!') && pattern[1] !== '(';
}
export function isPositivePattern(pattern: Pattern): boolean {
return !isNegativePattern(pattern);
}
export function getNegativePatterns(patterns: Pattern[]): Pattern[] {
return patterns.filter(isNegativePattern);
}
export function getPositivePatterns(patterns: Pattern[]): Pattern[] {
return patterns.filter(isPositivePattern);
}
/**
* Returns patterns that can be applied inside the current directory.
*
* @example
* // ['./*', '*', 'a/*']
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
*/
export function getPatternsInsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern));
}
/**
* Returns patterns to be expanded relative to (outside) the current directory.
*
* @example
* // ['../*', './../*']
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
*/
export function getPatternsOutsideCurrentDirectory(patterns: Pattern[]): Pattern[] {
return patterns.filter(isPatternRelatedToParentDirectory);
}
export function isPatternRelatedToParentDirectory(pattern: Pattern): boolean {
return pattern.startsWith('..') || pattern.startsWith('./..');
}
export function getBaseDirectory(pattern: Pattern): string {
return globParent(pattern, { flipBackslashes: false });
}
export function hasGlobStar(pattern: Pattern): boolean {
return pattern.includes(GLOBSTAR);
}
export function endsWithSlashGlobStar(pattern: Pattern): boolean {
return pattern.endsWith('/' + GLOBSTAR);
}
export function isAffectDepthOfReadingPattern(pattern: Pattern): boolean {
const basename = path.basename(pattern);
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
}
export function expandPatternsWithBraceExpansion(patterns: Pattern[]): Pattern[] {
return patterns.reduce((collection, pattern) => {
return collection.concat(expandBraceExpansion(pattern));
}, [] as Pattern[]);
}
export function expandBraceExpansion(pattern: Pattern): Pattern[] {
return micromatch.braces(pattern, {
expand: true,
nodupes: true
});
}
export function getPatternParts(pattern: Pattern, options: MicromatchOptions): Pattern[] {
let { parts } = micromatch.scan(pattern, {
...options,
parts: true
});
/**
* The scan method returns an empty array in some cases.
* See micromatch/picomatch#58 for more details.
*/
if (parts.length === 0) {
parts = [pattern];
}
/**
* The scan method does not return an empty part for the pattern with a forward slash.
* This is another part of micromatch/picomatch#58.
*/
if (parts[0].startsWith('/')) {
parts[0] = parts[0].slice(1);
parts.unshift('');
}
return parts;
}
export function makeRe(pattern: Pattern, options: MicromatchOptions): PatternRe {
return micromatch.makeRe(pattern, options);
}
export function convertPatternsToRe(patterns: Pattern[], options: MicromatchOptions): PatternRe[] {
return patterns.map((pattern) => makeRe(pattern, options));
}
export function matchAny(entry: string, patternsRe: PatternRe[]): boolean {
return patternsRe.some((patternRe) => patternRe.test(entry));
}
fast-glob-3.2.12/src/utils/stream.spec.ts 0000664 0000000 0000000 00000003022 14306550000 0020166 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as stream from 'stream';
import * as util from './stream';
describe('Utils → Stream', () => {
describe('.merge', () => {
it('should merge two streams into one stream', () => {
const first = new stream.PassThrough();
const second = new stream.PassThrough();
const expected = 3;
const mergedStream = util.merge([first, second]);
const actual = mergedStream.listenerCount('close');
assert.strictEqual(actual, expected);
});
it('should propagate errors into merged stream', (done) => {
const first = new stream.PassThrough();
const second = new stream.PassThrough();
const expected = [1, 2, 3];
const mergedStream = util.merge([first, second]);
const actual: number[] = [];
mergedStream.on('error', (error: number) => actual.push(error));
mergedStream.once('finish', () => {
assert.deepStrictEqual(actual, expected);
done();
});
first.emit('error', 1);
second.emit('error', 2);
mergedStream.emit('error', 3);
});
it('should propagate close event to source streams', (done) => {
const first = new stream.PassThrough();
const second = new stream.PassThrough();
const mergedStream = util.merge([first, second]);
const expected = [1, 2];
const actual: number[] = [];
first.once('close', () => actual.push(1));
second.once('close', () => actual.push(2));
mergedStream.once('finish', () => {
assert.deepStrictEqual(actual, expected);
done();
});
mergedStream.emit('close');
});
});
});
fast-glob-3.2.12/src/utils/stream.ts 0000664 0000000 0000000 00000001103 14306550000 0017233 0 ustar 00root root 0000000 0000000 import { Readable } from 'stream';
import * as merge2 from 'merge2';
export function merge(streams: Readable[]): NodeJS.ReadableStream {
const mergedStream = merge2(streams);
streams.forEach((stream) => {
stream.once('error', (error) => mergedStream.emit('error', error));
});
mergedStream.once('close', () => propagateCloseEventToSources(streams));
mergedStream.once('end', () => propagateCloseEventToSources(streams));
return mergedStream;
}
function propagateCloseEventToSources(streams: Readable[]): void {
streams.forEach((stream) => stream.emit('close'));
}
fast-glob-3.2.12/src/utils/string.spec.ts 0000664 0000000 0000000 00000001161 14306550000 0020203 0 ustar 00root root 0000000 0000000 import * as assert from 'assert';
import * as util from './string';
describe('Utils → String', () => {
describe('.isString', () => {
it('should return true', () => {
const actual = util.isString('');
assert.ok(actual);
});
it('should return false', () => {
const actual = util.isString(undefined as unknown as string);
assert.ok(!actual);
});
});
describe('.isEmpty', () => {
it('should return true', () => {
const actual = util.isEmpty('');
assert.ok(actual);
});
it('should return false', () => {
const actual = util.isEmpty('string');
assert.ok(!actual);
});
});
});
fast-glob-3.2.12/src/utils/string.ts 0000664 0000000 0000000 00000000254 14306550000 0017254 0 ustar 00root root 0000000 0000000 export function isString(input: unknown): input is string {
return typeof input === 'string';
}
export function isEmpty(input: string): boolean {
return input === '';
}
fast-glob-3.2.12/tsconfig.json 0000664 0000000 0000000 00000001261 14306550000 0016155 0 ustar 00root root 0000000 0000000 {
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"moduleResolution": "node",
"rootDir": "src",
"outDir": "out",
"strict": true,
"alwaysStrict": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"downlevelIteration": true,
"declaration": true,
"pretty": true
},
"include": [
"typings",
"src/**/*"
]
}