Skip to content

chore: replace globby with tinyglobby #9878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/typescript-estree/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@
"@typescript-eslint/types": "8.2.0",
"@typescript-eslint/visitor-keys": "8.2.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"tinyglobby": "^0.2.5",
"ts-api-utils": "^1.3.0"
},
"devDependencies": {
Expand Down
24 changes: 15 additions & 9 deletions packages/typescript-estree/src/parseSettings/resolveProjectList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import debug from 'debug';
import { sync as globSync } from 'globby';
import isGlob from 'is-glob';
import { globSync } from 'tinyglobby';

import type { CanonicalPath } from '../create-program/shared';
import {
Expand Down Expand Up @@ -93,16 +93,22 @@ export function resolveProjectList(
const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project));
const globProjects = sanitizedProjects.filter(project => isGlob(project));

let globProjectPaths: string[] = [];

if (globProjects.length > 0) {
// To ensure the order is correct, we need to glob for each pattern
// separately and then concatenate the results in patterns' order.
globProjectPaths = globProjects.flatMap(pattern =>
globSync([pattern, ...projectFolderIgnoreList], {
cwd: options.tsconfigRootDir,
dot: true,
}),
);
}

const uniqueCanonicalProjectPaths = new Map(
nonGlobProjects
.concat(
globProjects.length === 0
? []
: globSync([...globProjects, ...projectFolderIgnoreList], {
cwd: options.tsconfigRootDir,
dot: true,
}),
)
.concat(globProjectPaths)
.map(project => [
getCanonicalFileName(
ensureAbsolutePath(project, options.tsconfigRootDir),
Expand Down
39 changes: 21 additions & 18 deletions packages/typescript-estree/tests/lib/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { join, resolve } from 'node:path';

import type { CacheDurationSeconds } from '@typescript-eslint/types';
import debug from 'debug';
import * as globbyModule from 'globby';
import * as globbyModule from 'tinyglobby';
import type * as typescriptModule from 'typescript';

import * as parser from '../../src';
Expand Down Expand Up @@ -39,11 +39,11 @@ jest.mock('typescript', () => {
};
});

jest.mock('globby', () => {
const globby = jest.requireActual<typeof globbyModule>('globby');
jest.mock('tinyglobby', () => {
const globby = jest.requireActual<typeof globbyModule>('tinyglobby');
return {
...globby,
sync: jest.fn(globby.sync),
globSync: jest.fn(globby.globSync),
};
});

Expand All @@ -52,7 +52,7 @@ const hrtimeSpy = jest.spyOn(process, 'hrtime');
const createDefaultCompilerOptionsFromExtra = jest.mocked(
sharedParserUtilsModule.createDefaultCompilerOptionsFromExtra,
);
const globbySyncMock = jest.mocked(globbyModule.sync);
const globbySyncMock = jest.mocked(globbyModule.globSync);

/**
* Aligns paths between environments, node for windows uses `\`, for linux and mac uses `/`
Expand Down Expand Up @@ -685,6 +685,9 @@ describe('parseAndGenerateServices', () => {

describe('cacheLifetime', () => {
describe('glob', () => {
const project = ['./**/tsconfig.json', './**/tsconfig.extra.json'];
// `resolveProjectList()` runs a glob for each pattern
const expectedGlobCount = project.length;
function doParse(lifetime: CacheDurationSeconds): void {
parser.parseAndGenerateServices('const x = 1', {
cacheLifetime: {
Expand All @@ -693,52 +696,52 @@ describe('parseAndGenerateServices', () => {
disallowAutomaticSingleRunInference: true,
filePath: join(FIXTURES_DIR, 'file.ts'),
tsconfigRootDir: FIXTURES_DIR,
project: ['./**/tsconfig.json', './**/tsconfig.extra.json'],
project,
});
}

it('should cache globs if the lifetime is non-zero', () => {
doParse(30);
expect(globbySyncMock).toHaveBeenCalledTimes(1);
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);
doParse(30);
// shouldn't call globby again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(1);
// shouldn't glob again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);
});

it('should not cache globs if the lifetime is zero', () => {
doParse(0);
expect(globbySyncMock).toHaveBeenCalledTimes(1);
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);
doParse(0);
// should call globby again because we specified immediate cache expiry
expect(globbySyncMock).toHaveBeenCalledTimes(2);
// should glob again because we specified immediate cache expiry
expect(globbySyncMock).toHaveBeenCalledTimes(2 * expectedGlobCount);
});

it('should evict the cache if the entry expires', () => {
hrtimeSpy.mockReturnValueOnce([1, 0]);

doParse(30);
expect(globbySyncMock).toHaveBeenCalledTimes(1);
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);

// wow so much time has passed
hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]);

doParse(30);
// shouldn't call globby again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(2);
// shouldn't glob again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(2 * expectedGlobCount);
});

it('should infinitely cache if passed Infinity', () => {
hrtimeSpy.mockReturnValueOnce([1, 0]);

doParse('Infinity');
expect(globbySyncMock).toHaveBeenCalledTimes(1);
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);

// wow so much time has passed
hrtimeSpy.mockReturnValueOnce([Number.MAX_VALUE, 0]);

doParse('Infinity');
// shouldn't call globby again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(1);
// shouldn't glob again due to the caching
expect(globbySyncMock).toHaveBeenCalledTimes(expectedGlobCount);
});
});
});
Expand Down
26 changes: 24 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5916,13 +5916,13 @@ __metadata:
"@typescript-eslint/visitor-keys": 8.2.0
debug: ^4.3.4
glob: "*"
globby: ^11.1.0
is-glob: ^4.0.3
jest: 29.7.0
minimatch: ^9.0.4
prettier: ^3.2.5
rimraf: "*"
semver: ^7.6.0
tinyglobby: ^0.2.5
tmp: "*"
ts-api-utils: ^1.3.0
typescript: "*"
Expand Down Expand Up @@ -10458,6 +10458,18 @@ __metadata:
languageName: node
linkType: hard

"fdir@npm:^6.2.0":
version: 6.2.0
resolution: "fdir@npm:6.2.0"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 49f23efa8e045f5096cd3384ae5f9cbe49d7e4aa73714d00ffc9301f2231b4fe1ceb23006ba76a75770c4758ae537d774bf26642921cd7872e0e330a7e3839c9
languageName: node
linkType: hard

"feed@npm:^4.2.2":
version: 4.2.2
resolution: "feed@npm:4.2.2"
Expand Down Expand Up @@ -16047,7 +16059,7 @@ __metadata:
languageName: node
linkType: hard

"picomatch@npm:^4.0.1":
"picomatch@npm:^4.0.1, picomatch@npm:^4.0.2":
version: 4.0.2
resolution: "picomatch@npm:4.0.2"
checksum: a7a5188c954f82c6585720e9143297ccd0e35ad8072231608086ca950bee672d51b0ef676254af0788205e59bd4e4deb4e7708769226bed725bf13370a7d1464
Expand Down Expand Up @@ -19095,6 +19107,16 @@ __metadata:
languageName: node
linkType: hard

"tinyglobby@npm:^0.2.5":
version: 0.2.5
resolution: "tinyglobby@npm:0.2.5"
dependencies:
fdir: ^6.2.0
picomatch: ^4.0.2
checksum: 04d01bb603c10a94d44045236bab6a5402c59ead6a02a7317f119647d31c250394189cee152fddbf13e80496697987b29d90d75786b12f76358a936cff307d35
languageName: node
linkType: hard

"title-case@npm:^3.0.3":
version: 3.0.3
resolution: "title-case@npm:3.0.3"
Expand Down
Loading