diff --git a/package.json b/package.json index df00e30..9a265d7 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,11 @@ "homepage": "https://github.com/SuperchupuDev/tinyglobby#readme", "dependencies": { "fdir": "^6.4.2", - "picomatch": "^4.0.2" + "unmatch": "^1.0.1" }, "devDependencies": { "@biomejs/biome": "^1.9.4", "@types/node": "^22.9.0", - "@types/picomatch": "^3.0.1", "fs-fixture": "^2.6.0", "tsup": "^8.3.5", "typescript": "^5.6.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bdfc09..c6928dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,9 @@ importers: fdir: specifier: ^6.4.2 version: 6.4.2(picomatch@4.0.2) - picomatch: - specifier: ^4.0.2 - version: 4.0.2 + unmatch: + specifier: ^1.0.1 + version: 1.0.1 devDependencies: '@biomejs/biome': specifier: ^1.9.4 @@ -21,9 +21,6 @@ importers: '@types/node': specifier: ^22.9.0 version: 22.9.0 - '@types/picomatch': - specifier: ^3.0.1 - version: 3.0.1 fs-fixture: specifier: ^2.6.0 version: 2.6.0 @@ -355,9 +352,6 @@ packages: '@types/node@22.9.0': resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} - '@types/picomatch@3.0.1': - resolution: {integrity: sha512-1MRgzpzY0hOp9pW/kLRxeQhUWwil6gnrUYd3oEpeYBqp/FexhaCPv3F8LsYr47gtUU45fO2cm1dbwkSrHEo8Uw==} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -658,6 +652,10 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unmatch@1.0.1: + resolution: {integrity: sha512-Fe6Mi4RUci7pappunyfLbU0m+vq4xNkgjXXMmMzv58F3hQs3N4fg1oiAeGE7P6O8y6g+ck3RpbMn5CiLTHHysQ==} + engines: {node: '>=12'} + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -875,8 +873,6 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/picomatch@3.0.1': {} - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -1164,6 +1160,8 @@ snapshots: undici-types@6.19.8: {} + unmatch@1.0.1: {} + webidl-conversions@4.0.2: {} whatwg-url@7.1.0: diff --git a/src/index.ts b/src/index.ts index 0eb4dc1..e5ba773 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import path, { posix } from 'node:path'; import { type Options as FdirOptions, fdir } from 'fdir'; -import picomatch from 'picomatch'; +import match from 'unmatch'; import { isDynamicPattern } from './utils.ts'; export interface GlobOptions { @@ -101,12 +101,14 @@ function processPatterns( const matchPatterns: string[] = []; const ignorePatterns: string[] = []; + const unignorePatterns: string[] = []; for (const pattern of ignore) { - // don't handle negated patterns here for consistency with fast-glob if (!pattern.startsWith('!') || pattern[1] === '(') { const newPattern = normalizePattern(pattern, expandDirectories, cwd, properties, true); ignorePatterns.push(newPattern); + } else { + unignorePatterns.push(pattern.slice(1)); } } @@ -142,7 +144,7 @@ function processPatterns( } } - return { match: matchPatterns, ignore: ignorePatterns, transformed }; + return { match: matchPatterns, ignore: ignorePatterns, unignore: unignorePatterns, transformed }; } // TODO: this is slow, find a better way to do this @@ -175,18 +177,21 @@ function crawl(options: GlobOptions, cwd: string, sync: boolean) { const processed = processPatterns(options, cwd, properties); - const matcher = picomatch(processed.match, { + const unignoreMatcher = processed.unignore.length === 0 ? undefined : match(processed.unignore); + + const matcher = match(processed.match, { dot: options.dot, nocase: options.caseSensitiveMatch === false, - ignore: processed.ignore + ignore: processed.ignore, + onIgnore: unignoreMatcher ? result => unignoreMatcher(result.output) && match.constants.UNIGNORE : undefined }); - const ignore = picomatch(processed.ignore, { + const ignore = match(processed.ignore, { dot: options.dot, nocase: options.caseSensitiveMatch === false }); - const exclude = picomatch('*(../)**', { + const exclude = match('*(../)**', { dot: true, nocase: options.caseSensitiveMatch === false, ignore: processed.transformed diff --git a/src/utils.ts b/src/utils.ts index 4e6b345..94c192f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import picomatch from 'picomatch'; +import match from 'unmatch'; // #region convertPathToPattern const ESCAPED_WIN32_BACKSLASHES = /\\(?![()[\]{}!+@])/g; @@ -49,7 +49,7 @@ export function isDynamicPattern(pattern: string, options?: { caseSensitiveMatch return true; } - const scan = picomatch.scan(pattern); + const scan = match.scan(pattern); return scan.isGlob || scan.negated; } // #endregion diff --git a/test/index.test.ts b/test/index.test.ts index 3e29650..437cdc0 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -330,11 +330,9 @@ test('negative absolute patterns in options', async () => { assert.deepEqual(files2.sort(), ['a/b.txt', 'b/b.txt']); }); -// can't easily make them properly work right now -// but at least it's consistent with fast-glob this way test('negative patterns in ignore are ignored', async () => { const files = await glob({ patterns: ['**/*'], ignore: ['**/b.txt', '!a/b.txt'], cwd }); - assert.deepEqual(files.sort(), ['a/a.txt', 'b/a.txt']); + assert.deepEqual(files.sort(), ['a/a.txt', 'a/b.txt', 'b/a.txt']); const files2 = await glob({ patterns: ['**/*', '!**/b.txt', '!!a/b.txt'], cwd }); assert.deepEqual(files2.sort(), ['a/a.txt', 'b/a.txt']);