Skip to content

Commit 598f08a

Browse files
perf: replace glob package with node:fs glob
1 parent 66cdd8f commit 598f08a

7 files changed

Lines changed: 41 additions & 70 deletions

File tree

.github/DEVELOPMENT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ If you are having trouble, don't be afraid to [ask for help](./CONTRIBUTING.md#
55

66
## Prerequisites
77

8-
- [Node.js](https://nodejs.org/en/download) `^20.19.0 || >=22.12.0` (Node >=22 LTS recommended).
8+
- [Node.js](https://nodejs.org/en/download) `>=22.12.0` (Node >=22 LTS recommended).
99
- If you're new to installing Node, a tool like [nvm](https://github.com/nvm-sh/nvm#install-script) can help you manage multiple version installations.
1010
- npm (do not use yarn or pnpm for this repository).
1111
- [Google Chrome](https://www.google.com/chrome) is required to run browser tests locally.

.github/workflows/mocha.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
smoke:
2828
uses: ./.github/workflows/npm-script.yml
2929
with:
30-
node-versions: "20,22,24"
30+
node-versions: "22,24"
3131
npm-script: test-smoke
3232

3333
test-node-lts:
@@ -76,7 +76,7 @@ jobs:
7676
os: "ubuntu-latest,windows-latest"
7777
# We pin exact versions here per https://github.com/mochajs/mocha/issues/5052
7878
# Ref https://nodejs.org/en/about/previous-releases
79-
node-versions: "20.19.4,22.18.0,24.6.0"
79+
node-versions: "22.18.0,24.6.0"
8080
npm-script: test-node:${{ matrix.test-part }}
8181
coverage: ${{ matrix.coverage }}
8282

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Agent onboarding for the `mochajs/mocha` repository.
2121

2222
## Environment prerequisites
2323

24-
- Node.js: `^20.19.0 || >=22.12.0` (prefer Node 22 locally)
24+
- Node.js: `>=22.12.0` (prefer Node 22 locally)
2525
- npm (do not use pnpm/yarn for this repo)
2626
- Google Chrome (required for browser tests)
2727

@@ -98,7 +98,7 @@ CI partitions checks into separate jobs:
9898

9999
- `format:check`
100100
- `lint`
101-
- `test-smoke` (Node 20/22/24)
101+
- `test-smoke` (Node 22/24)
102102
- `test-node:*` matrix (interfaces/unit/integration/jsapi/requires/reporters/only)
103103
- browser tests (`test-browser` with ChromeHeadless)
104104
- `tsc`

lib/cli/lookup-files.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import fs from "node:fs";
88
import path from "node:path";
9-
import * as glob from "glob";
9+
import { Minimatch } from "minimatch";
1010
import {
1111
createNoFilesMatchPatternError,
1212
createMissingArgumentError,
@@ -72,7 +72,7 @@ export function lookupFiles(filepath, extensions = [], recursive = false) {
7272

7373
if (!fs.existsSync(filepath)) {
7474
let pattern;
75-
if (glob.hasMagic(filepath, { windowsPathsNoEscape: true })) {
75+
if (new Minimatch(filepath, { windowsPathsNoEscape: true }).hasMagic()) {
7676
// Handle glob as is without extensions
7777
pattern = filepath;
7878
} else {
@@ -84,13 +84,12 @@ export function lookupFiles(filepath, extensions = [], recursive = false) {
8484
debug("looking for files using glob pattern: %s", pattern);
8585
}
8686
files.push(
87-
...glob
88-
.sync(pattern, {
89-
nodir: true,
90-
windowsPathsNoEscape: true,
91-
})
92-
// glob@8 and earlier sorted results in en; glob@9 depends on OS sorting.
93-
// This preserves the older glob behavior.
87+
...fs
88+
.globSync(pattern, { withFileTypes: true })
89+
.filter((dirent) => !dirent.isDirectory())
90+
.map((dirent) => path.join(dirent.parentPath, dirent.name))
91+
// glob@8 and earlier sorted results in en; node:fs and glob@9 depend
92+
// on OS sorting. This preserves the older glob behavior.
9493
// https://github.com/mochajs/mocha/pull/5250/files#r1840469747
9594
.sort((a, b) => a.localeCompare(b, "en")),
9695
);

lib/cli/watch-run.cjs

Lines changed: 22 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@
33
const debug = require("debug")("mocha:cli:watch");
44
const path = require("node:path");
55
const chokidar = require("chokidar");
6-
const glob = require("glob");
76
const isPathInside = require("is-path-inside");
8-
const { minimatch } = require("minimatch");
7+
const { minimatch, Minimatch } = require("minimatch");
98
const Context = require("../context.js").Context;
109
const collectFiles = require("./collect-files.cjs");
1110
const { logSymbols } = require("../utils.cjs");
1211

1312
/**
1413
* @typedef {import('chokidar').FSWatcher} FSWatcher
15-
* @typedef {import('glob').Glob['patterns'][number]} Pattern
16-
* The `Pattern` class is not exported by the `glob` package.
17-
* Ref [link](../../node_modules/glob/dist/commonjs/pattern.d.ts).
1814
* @typedef {import('../mocha.cjs')} Mocha
1915
* @typedef {import('../types.d.ts').BeforeWatchRun} BeforeWatchRun
2016
* @typedef {import('../types.d.ts').FileCollectionOptions} FileCollectionOptions
@@ -177,66 +173,40 @@ function createPathFilter(globPaths, basePath) {
177173
// for checking if a path ends with `/**/*`
178174
const globEnd = path.join(path.sep, "**", "*");
179175

180-
/**
181-
* The current glob pattern to check.
182-
* @type {Pattern[]}
183-
*/
184176
const patterns = globPaths.flatMap((globPath) => {
185-
return new glob.Glob(globPath, {
177+
const mm = new Minimatch(globPath, {
186178
dot: true,
187-
magicalBraces: true,
188179
windowsPathsNoEscape: true,
189-
}).patterns;
190-
}, []);
191-
192-
// each pattern will have its own path because of the `magicalBraces` option
193-
for (const pattern of patterns) {
194-
debug("processing glob pattern: %s", pattern.globString());
195-
196-
/**
197-
* Path segments before the glob pattern.
198-
* @type {string[]}
199-
*/
200-
const segments = [];
201-
202-
/**
203-
* The current glob pattern to check.
204-
* @type {Pattern | null}
205-
*/
206-
let currentPattern = pattern;
207-
let isGlob = false;
208-
209-
do {
210-
// save string patterns until a non-string (glob or regexp) is matched
211-
const entry = currentPattern.pattern();
212-
const isString = typeof entry === "string";
213-
debug(
214-
"found %s pattern: %s",
215-
isString ? "string" : "glob or regexp",
216-
entry,
217-
);
218-
if (!isString) {
219-
// if the entry is a glob
220-
isGlob = true;
221-
break;
222-
}
180+
});
181+
return mm.set.map((parts, index) => ({
182+
parts,
183+
globString: mm.globSet[index],
184+
}));
185+
});
223186

224-
segments.push(entry);
187+
for (const { parts, globString } of patterns) {
188+
debug("processing glob pattern: %s", globString);
225189

226-
// go to next pattern
227-
} while ((currentPattern = currentPattern.rest()));
190+
let firstGlobIndex = parts.findIndex((part) => typeof part !== "string");
191+
const isGlob = firstGlobIndex !== -1;
228192
if (!isGlob) {
229-
debug("all subpatterns of %j processed", pattern.globString());
193+
firstGlobIndex = parts.length;
194+
debug("all subpatterns of %j processed", globString);
230195
}
231196

232-
// match `cleanPath` (path without the glob part) and its subdirectories
233-
const cleanPath = path.resolve(basePath, ...segments);
197+
// match `cleanPath` (path without the glob part) and its subdirectories.
198+
// After lots of errors, it turned out that globString is "/"-separated and keeps the absolute root (for example the
199+
// drive C:/ on Windows), so derive the static prefix from it.
200+
const cleanPath = path.resolve(
201+
basePath,
202+
globString.split("/").slice(0, firstGlobIndex).join("/"),
203+
);
234204
debug("clean path: %s", cleanPath);
235205
res.dir.paths.add(cleanPath);
236206
res.dir.globs.add(path.resolve(cleanPath, "**", "*"));
237207

238208
// match `absPath` and all of its contents
239-
const absPath = path.resolve(basePath, pattern.globString());
209+
const absPath = path.resolve(basePath, globString);
240210
debug("absolute path: %s", absPath);
241211
(isGlob ? res.match.globs : res.match.paths).add(absPath);
242212

package-lock.json

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"test": "./test"
4040
},
4141
"engines": {
42-
"node": "^20.19.0 || >=22.12.0"
42+
"node": ">=22.12.0"
4343
},
4444
"scripts": {
4545
"build": "rollup -c ./rollup.config.js",
@@ -92,7 +92,6 @@
9292
"debug": "^4.3.5",
9393
"diff": "^9.0.0",
9494
"find-up": "^5.0.0",
95-
"glob": "^13.0.0",
9695
"he": "^1.2.0",
9796
"is-path-inside": "^3.0.3",
9897
"is-unicode-supported": "^0.1.0",

0 commit comments

Comments
 (0)