fix: Support negation patterns when including/excluding files from ZIP files (#1517)

Co-authored-by: Aaron <aaronklinker1@gmail.com>
This commit is contained in:
Nishu
2025-06-15 04:38:04 +05:30
committed by GitHub
parent 1272e2ec74
commit 9ece49d226
4 changed files with 111 additions and 3 deletions
+22
View File
@@ -285,4 +285,26 @@ describe('Zipping', () => {
expect(await project.fileExists(sourcesZip)).toBe(false);
},
);
it('should include files in the zip when negated in zip.exclude', async () => {
const project = new TestProject({
name: 'test',
version: '1.0.0',
});
project.addFile(
'entrypoints/background.ts',
'export default defineBackground(() => {});',
);
const unzipDir = project.resolvePath('.output/test-1.0.0-chrome');
const sourcesZip = project.resolvePath('.output/test-1.0.0-chrome.zip');
await project.zip({
zip: {
exclude: ['**/*.json', '!manifest.json'],
},
});
await extract(sourcesZip, { dir: unzipDir });
expect(await project.fileExists(unzipDir, 'manifest.json')).toBe(true);
});
});
@@ -0,0 +1,46 @@
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);
});
});
@@ -0,0 +1,40 @@
import { minimatch, MinimatchOptions } from 'minimatch';
/**
* Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
* patterns.
*
* Supports negated patterns, the order does not matter. If your `search` string
* matches any of the negative patterns, it will return `false`.
*
* @example
* ```ts
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
* ```
*/
export function minimatchMultiple(
search: string,
patterns: string[] | undefined,
options?: MinimatchOptions,
): boolean {
if (patterns == null) return false;
const negatePatterns: string[] = [];
const positivePatterns: string[] = [];
for (const pattern of patterns) {
if (pattern[0] === '!') negatePatterns.push(pattern.slice(1));
else positivePatterns.push(pattern);
}
if (
negatePatterns.some((negatePattern) =>
minimatch(search, negatePattern, options),
)
)
return false;
return positivePatterns.some((positivePattern) =>
minimatch(search, positivePattern, options),
);
}
+3 -3
View File
@@ -3,7 +3,6 @@ import path from 'node:path';
import fs from 'fs-extra';
import { safeFilename } from './utils/strings';
import { getPackageJson } from './utils/package';
import { minimatch } from 'minimatch';
import { formatDuration } from './utils/time';
import { printFileList } from './utils/log/printFileList';
import { findEntrypoints, internalBuild } from './utils/building';
@@ -11,6 +10,7 @@ import { registerWxt, wxt } from './wxt';
import JSZip from 'jszip';
import glob from 'fast-glob';
import { normalizePath } from './utils/paths';
import { minimatchMultiple } from './utils/minimatch-multiple';
/**
* Build and zip the extension for distribution.
@@ -122,8 +122,8 @@ async function zipDir(
})
).filter((relativePath) => {
return (
options?.include?.some((pattern) => minimatch(relativePath, pattern)) ||
!options?.exclude?.some((pattern) => minimatch(relativePath, pattern))
minimatchMultiple(relativePath, options?.include) ||
!minimatchMultiple(relativePath, options?.exclude)
);
});
const filesToZip = [