feat(providence): support fullPath notation in optimisedGlob

This commit is contained in:
Thijs Louisse 2024-10-21 18:46:40 +02:00 committed by Thijs Louisse
parent 2f3f4276fb
commit 6ec74dc58b
3 changed files with 67 additions and 16 deletions

View file

@ -0,0 +1,5 @@
---
'providence-analytics': patch
---
support fullPath notation in optimisedGlob

View file

@ -130,6 +130,14 @@ function toUniqueArray(arr) {
return Array.from(new Set(arr)); return Array.from(new Set(arr));
} }
/**
* @param {string} glob
* @returns {boolean}
*/
function isRootGlob(glob) {
return glob.startsWith('/') || glob.startsWith('!/') || Boolean(glob.match(/^([A-Z]:\\|\\\\)/));
}
/** /**
* @param {DirentWithPath} dirent * @param {DirentWithPath} dirent
* @param {{cwd:string}} cfg * @param {{cwd:string}} cfg
@ -162,7 +170,10 @@ function postprocessOptions(matchedEntries, options) {
} }
if (options.absolute) { if (options.absolute) {
filteredPaths = filteredPaths.map(f => toPosixPath(path.join(options.cwd, f))); filteredPaths = filteredPaths.map(f => {
const isRootPath = isRootGlob(f);
return isRootPath ? toPosixPath(f) : toPosixPath(path.join(options.cwd, f));
});
if (process.platform === 'win32') { if (process.platform === 'win32') {
const driveChar = path.win32.resolve(options.cwd).slice(0, 1).toUpperCase(); const driveChar = path.win32.resolve(options.cwd).slice(0, 1).toUpperCase();
filteredPaths = filteredPaths.map(f => `${driveChar}:${f}`); filteredPaths = filteredPaths.map(f => `${driveChar}:${f}`);
@ -183,19 +194,17 @@ function postprocessOptions(matchedEntries, options) {
const getStartPath = memoize( const getStartPath = memoize(
/** /**
* @param {string} glob * @param {string} glob
* @returns {string}
*/ */
glob => { glob => {
const reservedChars = ['?', '[', ']', '{', '}', ',', '.', '*']; const reservedChars = ['?', '[', ']', '{', '}', ',', '.', '*'];
let hasFoundReservedChar = false; const startPathParts = [];
return glob for (const part of glob.split('/')) {
.split('/') const hasReservedChar = reservedChars.some(reservedChar => part.includes(reservedChar));
.map(part => { if (hasReservedChar) break;
if (hasFoundReservedChar) return undefined; startPathParts.push(part);
hasFoundReservedChar = reservedChars.some(reservedChar => part.includes(reservedChar)); }
return hasFoundReservedChar ? undefined : part; return startPathParts.join('/');
})
.filter(Boolean)
.join('/');
}, },
); );
@ -315,9 +324,12 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
const ignoreGlobs = options.ignore.map((/** @type {string} */ g) => const ignoreGlobs = options.ignore.map((/** @type {string} */ g) =>
g.startsWith('!') ? g : `!${g}`, g.startsWith('!') ? g : `!${g}`,
); );
const globs = toUniqueArray([...regularGlobs, ...ignoreGlobs]);
const optionsNotSupportedByNativeGlob = ['onlyDirectories', 'dot']; const optionsNotSupportedByNativeGlob = ['onlyDirectories', 'dot'];
const doesConfigAllowNative = !optionsNotSupportedByNativeGlob.some(opt => options[opt]); const hasGlobWithFullPath = globs.some(isRootGlob);
const doesConfigAllowNative =
!optionsNotSupportedByNativeGlob.some(opt => options[opt]) && !hasGlobWithFullPath;
if (isExperimentalFsGlobEnabled && options.fs.promises.glob && doesConfigAllowNative) { if (isExperimentalFsGlobEnabled && options.fs.promises.glob && doesConfigAllowNative) {
const negativeGlobs = [...ignoreGlobs, ...regularGlobs.filter(r => r.startsWith('!'))].map(r => const negativeGlobs = [...ignoreGlobs, ...regularGlobs.filter(r => r.startsWith('!'))].map(r =>
r.slice(1), r.slice(1),
@ -350,8 +362,6 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
); );
} }
const globs = toUniqueArray([...regularGlobs, ...ignoreGlobs]);
/** @type {RegExp[]} */ /** @type {RegExp[]} */
const matchRegexesNegative = []; const matchRegexesNegative = [];
/** @type {RegExp[]} */ /** @type {RegExp[]} */
@ -377,11 +387,13 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
// Search for the "deepest" starting point in the filesystem that we can use to search the fs // Search for the "deepest" starting point in the filesystem that we can use to search the fs
const startPath = getStartPath(globNormalized); const startPath = getStartPath(globNormalized);
const fullStartPath = path.join(options.cwd, startPath); const isRootPath = isRootGlob(startPath);
const cwd = isRootPath ? '/' : options.cwd;
const fullStartPath = path.join(cwd, startPath);
try { try {
const allDirEntsRelativeToCwd = await getAllDirentsRelativeToCwd(fullStartPath, { const allDirEntsRelativeToCwd = await getAllDirentsRelativeToCwd(fullStartPath, {
cwd: options.cwd, cwd,
fs: options.fs, fs: options.fs,
}); });

View file

@ -40,6 +40,8 @@ async function runOptimisedGlobAndCheckGlobbyParity(patterns, options) {
); );
} }
console.debug({ optimisedGlobResult, globbyResult });
expect(optimisedGlobResult).to.deep.equal(globbyResult); expect(optimisedGlobResult).to.deep.equal(globbyResult);
return optimisedGlobResult; return optimisedGlobResult;
@ -302,6 +304,38 @@ function runSuiteForOptimisedGlob() {
).to.throw(); ).to.throw();
}); });
}); });
describe('Edge cases', () => {
it('allows prefixing a system path (regardless of cwd)', async () => {
const files = await runOptimisedGlobAndCheckGlobbyParity(
'/fakeFs/my/folder/*/some/file.{js,d.ts}',
testCfg,
);
expect(files).to.deep.equal([
'/fakeFs/my/folder/lvl1/some/file.d.ts',
'/fakeFs/my/folder/lvl1/some/file.js',
]);
// With non-existing cwd
const files2 = await runOptimisedGlobAndCheckGlobbyParity(
'/fakeFs/my/folder/*/some/file.{js,d.ts}',
{
...testCfg,
cwd: '/nonExisting/path', // this will not exist
},
);
expect(files2).to.deep.equal([
'/fakeFs/my/folder/lvl1/some/file.d.ts',
'/fakeFs/my/folder/lvl1/some/file.js',
]);
// // With negative globs: this is not supported by globby
// const files3 = await runOptimisedGlobAndCheckGlobbyParity(
// ['!/fakeFs/my/**/*.d.ts', '/fakeFs/my/folder/*/some/file.{js,d.ts}'],
// testCfg,
// );
// expect(files3).to.deep.equal(['/fakeFs/my/folder/lvl1/some/file.js']);
});
});
}); });
} }