fix: Support negation patterns when including/excluding files from ZIP files (#1517)
Co-authored-by: Aaron <aaronklinker1@gmail.com>
This commit is contained in:
@@ -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,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 = [
|
||||
|
||||
Reference in New Issue
Block a user