From d64b64f39ed87ed976ff01e070669fc61bdab937 Mon Sep 17 00:00:00 2001 From: Aditi <80719906+Adiii-15@users.noreply.github.com> Date: Wed, 18 Mar 2026 07:22:38 -0700 Subject: [PATCH] chore: replace minimatch with picomatch (#2188) Co-authored-by: Aaron Co-authored-by: Rizky Mirzaviandy Priambodo --- packages/wxt/package.json | 3 +- .../__tests__/minimatch-multiple.test.ts | 46 ------------ .../__tests__/picomatch-multiple.test.ts | 71 +++++++++++++++++++ .../core/utils/building/find-entrypoints.ts | 4 +- ...atch-multiple.ts => picomatch-multiple.ts} | 16 ++--- packages/wxt/src/core/zip.ts | 6 +- packages/wxt/src/types.ts | 6 +- pnpm-lock.yaml | 14 +++- 8 files changed, 100 insertions(+), 66 deletions(-) delete mode 100644 packages/wxt/src/core/utils/__tests__/minimatch-multiple.test.ts create mode 100644 packages/wxt/src/core/utils/__tests__/picomatch-multiple.test.ts rename packages/wxt/src/core/utils/{minimatch-multiple.ts => picomatch-multiple.ts} (62%) diff --git a/packages/wxt/package.json b/packages/wxt/package.json index 5f6a3878..9ae1c4f4 100644 --- a/packages/wxt/package.json +++ b/packages/wxt/package.json @@ -43,7 +43,6 @@ "jszip": "^3.10.1", "linkedom": "^0.18.12", "magicast": "^0.5.2", - "minimatch": "^10.2.4", "nano-spawn": "^2.0.0", "nanospinner": "^1.2.2", "normalize-path": "^3.0.0", @@ -52,6 +51,7 @@ "open": "^11.0.0", "perfect-debounce": "^2.1.0", "picocolors": "^1.1.1", + "picomatch": "^4.0.3", "prompts": "^2.4.2", "publish-browser-extension": "^2.3.0 || ^3.0.2 || ^4.0.4", "scule": "^1.3.0", @@ -74,6 +74,7 @@ "@types/lodash.merge": "^4.6.9", "@types/node": "^20.17.6", "@types/normalize-path": "^3.0.2", + "@types/picomatch": "^4.0.2", "@types/prompts": "^2.4.9", "eslint": "^10.0.0", "extract-zip": "^2.0.1", diff --git a/packages/wxt/src/core/utils/__tests__/minimatch-multiple.test.ts b/packages/wxt/src/core/utils/__tests__/minimatch-multiple.test.ts deleted file mode 100644 index 34c078e1..00000000 --- a/packages/wxt/src/core/utils/__tests__/minimatch-multiple.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { minimatchMultiple } from '../minimatch-multiple'; - -describe('minimatchMultiple', () => { - it('should return false if the pattern array is undefined', () => { - const patterns = undefined; - const search = 'test.json'; - - expect(minimatchMultiple(search, patterns)).toBe(false); - }); - - it('should return false if the pattern array is empty', () => { - const patterns: string[] = []; - const search = 'test.json'; - - expect(minimatchMultiple(search, patterns)).toBe(false); - }); - - it('should return true if the pattern array contains a match', () => { - const patterns = ['test.yml', 'test.json']; - const search = 'test.json'; - - expect(minimatchMultiple(search, patterns)).toBe(true); - }); - - it('should return false if the pattern array does not contain a match', () => { - const patterns = ['test.yml', 'test.json']; - const search = 'test.txt'; - - expect(minimatchMultiple(search, patterns)).toBe(false); - }); - - it('should return false if the pattern matches a negative pattern', () => { - const patterns = ['test.*', '!test.json']; - const search = 'test.json'; - - expect(minimatchMultiple(search, patterns)).toBe(false); - }); - - it('should return false if the pattern matches a negative pattern, regardless of order', () => { - const patterns = ['!test.json', 'test.*']; - const search = 'test.json'; - - expect(minimatchMultiple(search, patterns)).toBe(false); - }); -}); diff --git a/packages/wxt/src/core/utils/__tests__/picomatch-multiple.test.ts b/packages/wxt/src/core/utils/__tests__/picomatch-multiple.test.ts new file mode 100644 index 00000000..0e154bfc --- /dev/null +++ b/packages/wxt/src/core/utils/__tests__/picomatch-multiple.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect } from 'vitest'; +import { picomatchMultiple } from '../picomatch-multiple'; + +describe('picomatchMultiple', () => { + it('should return false if the pattern array is undefined', () => { + const patterns = undefined; + const search = 'test.json'; + + expect(picomatchMultiple(search, patterns)).toBe(false); + }); + + it('should return false if the pattern array is empty', () => { + const patterns: string[] = []; + const search = 'test.json'; + + expect(picomatchMultiple(search, patterns)).toBe(false); + }); + + it('should return true if the pattern array contains a match', () => { + const patterns = ['test.yml', 'test.json']; + const search = 'test.json'; + + expect(picomatchMultiple(search, patterns)).toBe(true); + }); + + it('should return false if the pattern array does not contain a match', () => { + const patterns = ['test.yml', 'test.json']; + const search = 'test.txt'; + + expect(picomatchMultiple(search, patterns)).toBe(false); + }); + + it('should return false if the pattern matches a negative pattern', () => { + const patterns = ['test.*', '!test.json']; + const search = 'test.json'; + + expect(picomatchMultiple(search, patterns)).toBe(false); + }); + + it('should return false if the pattern matches a negative pattern, regardless of order', () => { + const patterns = ['!test.json', 'test.*']; + const search = 'test.json'; + + expect(picomatchMultiple(search, patterns)).toBe(false); + }); + + it('should support extglob-like extension matching', () => { + const patterns = ['content.[jt]s?(x)']; + + expect(picomatchMultiple('content.ts', patterns)).toBe(true); + expect(picomatchMultiple('content.jsx', patterns)).toBe(true); + expect(picomatchMultiple('content.css', patterns)).toBe(false); + }); + + it('should support nested paths', () => { + const patterns = ['foo/**/*.ts']; + + expect(picomatchMultiple('foo/bar/baz.ts', patterns)).toBe(true); + expect(picomatchMultiple('foo/bar/baz.js', patterns)).toBe(false); + }); + + it('should preserve include/exclude interaction used by zip filtering', () => { + const include = ['special.txt']; + const exclude = ['**/*.txt']; + const search = 'special.txt'; + const shouldInclude = + picomatchMultiple(search, include) || !picomatchMultiple(search, exclude); + + expect(shouldInclude).toBe(true); + }); +}); diff --git a/packages/wxt/src/core/utils/building/find-entrypoints.ts b/packages/wxt/src/core/utils/building/find-entrypoints.ts index a81e1704..f27cc4dc 100644 --- a/packages/wxt/src/core/utils/building/find-entrypoints.ts +++ b/packages/wxt/src/core/utils/building/find-entrypoints.ts @@ -13,7 +13,7 @@ import { UnlistedScriptEntrypoint, } from '../../../types'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; -import { minimatch } from 'minimatch'; +import picomatch from 'picomatch'; import { parseHTML } from 'linkedom'; import JSON5 from 'json5'; import { glob } from 'tinyglobby'; @@ -61,7 +61,7 @@ export async function findEntrypoints(): Promise { const inputPath = resolve(wxt.config.entrypointsDir, relativePath); const name = getEntrypointName(wxt.config.entrypointsDir, inputPath); const matchingGlob = pathGlobs.find((glob) => - minimatch(relativePath, glob), + picomatch(glob)(relativePath), ); if (matchingGlob) { const type = PATH_GLOB_TO_TYPE_MAP[matchingGlob]; diff --git a/packages/wxt/src/core/utils/minimatch-multiple.ts b/packages/wxt/src/core/utils/picomatch-multiple.ts similarity index 62% rename from packages/wxt/src/core/utils/minimatch-multiple.ts rename to packages/wxt/src/core/utils/picomatch-multiple.ts index 02beef71..0b728f3a 100644 --- a/packages/wxt/src/core/utils/minimatch-multiple.ts +++ b/packages/wxt/src/core/utils/picomatch-multiple.ts @@ -1,7 +1,7 @@ -import { minimatch, MinimatchOptions } from 'minimatch'; +import picomatch, { PicomatchOptions } from 'picomatch'; /** - * Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple + * Run [`picomatch`](https://npmjs.com/package/picomatch) against multiple * patterns. * * Supports negated patterns, the order does not matter. If your `search` string @@ -9,14 +9,14 @@ import { minimatch, MinimatchOptions } from 'minimatch'; * * @example * ```ts - * minimatchMultiple('a.json', ['*.json', '!b.json']); // => true - * minimatchMultiple('b.json', ['*.json', '!b.json']); // => false + * picomatchMultiple('a.json', ['*.json', '!b.json']); // => true + * picomatchMultiple('b.json', ['*.json', '!b.json']); // => false * ```; */ -export function minimatchMultiple( +export function picomatchMultiple( search: string, patterns: string[] | undefined, - options?: MinimatchOptions, + options?: PicomatchOptions, ): boolean { if (patterns == null) return false; @@ -29,12 +29,12 @@ export function minimatchMultiple( if ( negatePatterns.some((negatePattern) => - minimatch(search, negatePattern, options), + picomatch(negatePattern, options)(search), ) ) return false; return positivePatterns.some((positivePattern) => - minimatch(search, positivePattern, options), + picomatch(positivePattern, options)(search), ); } diff --git a/packages/wxt/src/core/zip.ts b/packages/wxt/src/core/zip.ts index 71595f6b..119dcc5e 100644 --- a/packages/wxt/src/core/zip.ts +++ b/packages/wxt/src/core/zip.ts @@ -11,7 +11,7 @@ import { registerWxt, wxt } from './wxt'; import JSZip from 'jszip'; import { glob } from 'tinyglobby'; import { normalizePath } from './utils'; -import { minimatchMultiple } from './utils/minimatch-multiple'; +import { picomatchMultiple } from './utils/picomatch-multiple'; /** * Build and zip the extension for distribution. @@ -125,8 +125,8 @@ async function zipDir( }) ).filter((relativePath) => { return ( - minimatchMultiple(relativePath, options?.include) || - !minimatchMultiple(relativePath, options?.exclude) + picomatchMultiple(relativePath, options?.include) || + !picomatchMultiple(relativePath, options?.exclude) ); }); const filesToZip = [ diff --git a/packages/wxt/src/types.ts b/packages/wxt/src/types.ts index 9a9723a3..6001f86f 100644 --- a/packages/wxt/src/types.ts +++ b/packages/wxt/src/types.ts @@ -212,7 +212,7 @@ export interface InlineConfig { */ sourcesRoot?: string; /** - * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to + * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to * include when creating a ZIP of all your source code for Firefox. Patterns * are relative to your `config.zip.sourcesRoot`. * @@ -226,7 +226,7 @@ export interface InlineConfig { */ includeSources?: string[]; /** - * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to + * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to * exclude when creating a ZIP of all your source code for Firefox. Patterns * are relative to your `config.zip.sourcesRoot`. * @@ -239,7 +239,7 @@ export interface InlineConfig { */ excludeSources?: string[]; /** - * [Minimatch](https://www.npmjs.com/package/minimatch) patterns of files to + * [Picomatch](https://www.npmjs.com/package/picomatch) patterns of files to * exclude when zipping the extension. * * @example diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ae9d302..2df5f796 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -495,9 +495,6 @@ importers: magicast: specifier: ^0.5.2 version: 0.5.2 - minimatch: - specifier: ^10.2.4 - version: 10.2.4 nano-spawn: specifier: ^2.0.0 version: 2.0.0 @@ -522,6 +519,9 @@ importers: picocolors: specifier: ^1.1.1 version: 1.1.1 + picomatch: + specifier: ^4.0.3 + version: 4.0.3 prompts: specifier: ^2.4.2 version: 2.4.2 @@ -559,6 +559,9 @@ importers: '@types/normalize-path': specifier: ^3.0.2 version: 3.0.2 + '@types/picomatch': + specifier: ^4.0.2 + version: 4.0.2 '@types/prompts': specifier: ^2.4.9 version: 2.4.9 @@ -2195,6 +2198,9 @@ packages: '@types/normalize-path@3.0.2': resolution: {integrity: sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==} + '@types/picomatch@4.0.2': + resolution: {integrity: sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==} + '@types/prompts@2.4.9': resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} @@ -6299,6 +6305,8 @@ snapshots: '@types/normalize-path@3.0.2': {} + '@types/picomatch@4.0.2': {} + '@types/prompts@2.4.9': dependencies: '@types/node': 20.19.32