Skip to content

Commit 9ece49d

Browse files
fix: Support negation patterns when including/excluding files from ZIP files (#1517)
Co-authored-by: Aaron <[email protected]>
1 parent 1272e2e commit 9ece49d

File tree

4 files changed

+111
-3
lines changed

4 files changed

+111
-3
lines changed

packages/wxt/e2e/tests/zip.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,26 @@ describe('Zipping', () => {
285285
expect(await project.fileExists(sourcesZip)).toBe(false);
286286
},
287287
);
288+
289+
it('should include files in the zip when negated in zip.exclude', async () => {
290+
const project = new TestProject({
291+
name: 'test',
292+
version: '1.0.0',
293+
});
294+
project.addFile(
295+
'entrypoints/background.ts',
296+
'export default defineBackground(() => {});',
297+
);
298+
const unzipDir = project.resolvePath('.output/test-1.0.0-chrome');
299+
const sourcesZip = project.resolvePath('.output/test-1.0.0-chrome.zip');
300+
301+
await project.zip({
302+
zip: {
303+
exclude: ['**/*.json', '!manifest.json'],
304+
},
305+
});
306+
307+
await extract(sourcesZip, { dir: unzipDir });
308+
expect(await project.fileExists(unzipDir, 'manifest.json')).toBe(true);
309+
});
288310
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { minimatchMultiple } from '../minimatch-multiple';
3+
4+
describe('minimatchMultiple', () => {
5+
it('should return false if the pattern array is undefined', () => {
6+
const patterns = undefined;
7+
const search = 'test.json';
8+
9+
expect(minimatchMultiple(search, patterns)).toBe(false);
10+
});
11+
12+
it('should return false if the pattern array is empty', () => {
13+
const patterns: string[] = [];
14+
const search = 'test.json';
15+
16+
expect(minimatchMultiple(search, patterns)).toBe(false);
17+
});
18+
19+
it('should return true if the pattern array contains a match', () => {
20+
const patterns = ['test.yml', 'test.json'];
21+
const search = 'test.json';
22+
23+
expect(minimatchMultiple(search, patterns)).toBe(true);
24+
});
25+
26+
it('should return false if the pattern array does not contain a match', () => {
27+
const patterns = ['test.yml', 'test.json'];
28+
const search = 'test.txt';
29+
30+
expect(minimatchMultiple(search, patterns)).toBe(false);
31+
});
32+
33+
it('should return false if the pattern matches a negative pattern', () => {
34+
const patterns = ['test.*', '!test.json'];
35+
const search = 'test.json';
36+
37+
expect(minimatchMultiple(search, patterns)).toBe(false);
38+
});
39+
40+
it('should return false if the pattern matches a negative pattern, regardless of order', () => {
41+
const patterns = ['!test.json', 'test.*'];
42+
const search = 'test.json';
43+
44+
expect(minimatchMultiple(search, patterns)).toBe(false);
45+
});
46+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { minimatch, MinimatchOptions } from 'minimatch';
2+
3+
/**
4+
* Run [`minimatch`](https://npmjs.com/package/minimatch) against multiple
5+
* patterns.
6+
*
7+
* Supports negated patterns, the order does not matter. If your `search` string
8+
* matches any of the negative patterns, it will return `false`.
9+
*
10+
* @example
11+
* ```ts
12+
* minimatchMultiple('a.json', ['*.json', '!b.json']); // => true
13+
* minimatchMultiple('b.json', ['*.json', '!b.json']); // => false
14+
* ```
15+
*/
16+
export function minimatchMultiple(
17+
search: string,
18+
patterns: string[] | undefined,
19+
options?: MinimatchOptions,
20+
): boolean {
21+
if (patterns == null) return false;
22+
23+
const negatePatterns: string[] = [];
24+
const positivePatterns: string[] = [];
25+
for (const pattern of patterns) {
26+
if (pattern[0] === '!') negatePatterns.push(pattern.slice(1));
27+
else positivePatterns.push(pattern);
28+
}
29+
30+
if (
31+
negatePatterns.some((negatePattern) =>
32+
minimatch(search, negatePattern, options),
33+
)
34+
)
35+
return false;
36+
37+
return positivePatterns.some((positivePattern) =>
38+
minimatch(search, positivePattern, options),
39+
);
40+
}

packages/wxt/src/core/zip.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import path from 'node:path';
33
import fs from 'fs-extra';
44
import { safeFilename } from './utils/strings';
55
import { getPackageJson } from './utils/package';
6-
import { minimatch } from 'minimatch';
76
import { formatDuration } from './utils/time';
87
import { printFileList } from './utils/log/printFileList';
98
import { findEntrypoints, internalBuild } from './utils/building';
109
import { registerWxt, wxt } from './wxt';
1110
import JSZip from 'jszip';
1211
import glob from 'fast-glob';
1312
import { normalizePath } from './utils/paths';
13+
import { minimatchMultiple } from './utils/minimatch-multiple';
1414

1515
/**
1616
* Build and zip the extension for distribution.
@@ -122,8 +122,8 @@ async function zipDir(
122122
})
123123
).filter((relativePath) => {
124124
return (
125-
options?.include?.some((pattern) => minimatch(relativePath, pattern)) ||
126-
!options?.exclude?.some((pattern) => minimatch(relativePath, pattern))
125+
minimatchMultiple(relativePath, options?.include) ||
126+
!minimatchMultiple(relativePath, options?.exclude)
127127
);
128128
});
129129
const filesToZip = [

0 commit comments

Comments
 (0)