fix(providence): normalization of native glob dirent.parentPath for optimisedGlob

This commit is contained in:
Thijs Louisse 2024-10-24 16:50:22 +02:00 committed by Thijs Louisse
parent 2d4fb0ecdb
commit 6f3137c963
3 changed files with 85 additions and 41 deletions

View file

@ -0,0 +1,5 @@
---
'providence-analytics': patch
---
fix normalization of native glob dirent.parentPath for optimisedGlob

View file

@ -144,7 +144,13 @@ function isRootGlob(glob) {
* @returns {string}
*/
function direntToLocalPath(dirent, { cwd }) {
const folder = (dirent.parentPath || dirent.path).replace(/(^.*)\/(.*\..*$)/, '$1');
const parentPath = toPosixPath(dirent.parentPath || dirent.path);
// Since `fs.glob` can return parent paths with files included, we need to strip the file part
const parts = parentPath.split('/');
const lastPart = parts[parts.length - 1];
const isLastPartFile = !lastPart.startsWith('.') && lastPart.split('.').length > 1;
const folder = isLastPartFile ? parts.slice(0, parts.length - 1).join('/') : parentPath;
return toPosixPath(path.join(folder, dirent.name)).replace(
new RegExp(`^${toPosixPath(cwd)}/`),
'',
@ -270,7 +276,7 @@ const getAllDirentsRelativeToCwd = memoize(
/**
* @param {string|string[]} globOrGlobs
* @param {{ fs: FsLike; cwd: string; exclude?: Function; stats?: boolean }} cfg
* @param {Partial<FastGlobtions> & {exclude?:Function; stats: boolean; cwd:string}} cfg
* @returns {Promise<string[]|DirentWithPath[]>}
*/
async function nativeGlob(globOrGlobs, { fs, cwd, exclude, stats }) {
@ -288,6 +294,54 @@ async function nativeGlob(globOrGlobs, { fs, cwd, exclude, stats }) {
return results;
}
/**
*
* @param {{globs: string[]; ignoreGlobs: string[]; options: FastGlobtions; regularGlobs:string[]}} config
* @returns {Promise<string[]|void>}
*/
async function getNativeGlobResults({ globs, options, ignoreGlobs, regularGlobs }) {
const optionsNotSupportedByNativeGlob = ['onlyDirectories', 'dot'];
const hasGlobWithFullPath = globs.some(isRootGlob);
const doesConfigAllowNative =
!optionsNotSupportedByNativeGlob.some(opt => options[opt]) && !hasGlobWithFullPath;
if (!doesConfigAllowNative) return undefined;
const negativeGlobs = [...ignoreGlobs, ...regularGlobs.filter(r => r.startsWith('!'))].map(r =>
r.slice(1),
);
const negativeResults = negativeGlobs.length
? // @ts-ignore
/** @type {string[]} */ (await nativeGlob(negativeGlobs, options))
: [];
const positiveGlobs = regularGlobs.filter(r => !r.startsWith('!'));
const result = /** @type {DirentWithPath[]} */ (
await nativeGlob(positiveGlobs, {
cwd: /** @type {string} */ (options.cwd),
fs: options.fs,
stats: true,
// we cannot use the exclude option here, because it's not working correctly
})
);
const direntsFiltered = result.filter(
dirent =>
!negativeResults.includes(
direntToLocalPath(dirent, { cwd: /** @type {string} */ (options.cwd) }),
),
);
return postprocessOptions(
direntsFiltered.map(dirent => ({
dirent,
relativeToCwdPath: direntToLocalPath(dirent, { cwd: /** @type {string} */ (options.cwd) }),
})),
options,
);
}
/**
* Lightweight glob implementation.
* It's a drop-in replacement for globby, but it's faster, a few hundred lines of code and has no dependencies.
@ -326,40 +380,10 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
);
const globs = toUniqueArray([...regularGlobs, ...ignoreGlobs]);
const optionsNotSupportedByNativeGlob = ['onlyDirectories', 'dot'];
const hasGlobWithFullPath = globs.some(isRootGlob);
const doesConfigAllowNative =
!optionsNotSupportedByNativeGlob.some(opt => options[opt]) && !hasGlobWithFullPath;
if (isExperimentalFsGlobEnabled && options.fs.promises.glob && doesConfigAllowNative) {
const negativeGlobs = [...ignoreGlobs, ...regularGlobs.filter(r => r.startsWith('!'))].map(r =>
r.slice(1),
);
const negativeResults = negativeGlobs.length
? /** @type {string[]} */ (await nativeGlob(negativeGlobs, options))
: [];
const positiveGlobs = regularGlobs.filter(r => !r.startsWith('!'));
const result = /** @type {DirentWithPath[]} */ (
await nativeGlob(positiveGlobs, {
cwd: options.cwd,
fs: options.fs,
stats: true,
// we cannot use the exclude option here, because it's not working correctly
})
);
const direntsFiltered = result.filter(
dirent => !negativeResults.includes(direntToLocalPath(dirent, { cwd: options.cwd })),
);
return postprocessOptions(
direntsFiltered.map(dirent => ({
dirent,
relativeToCwdPath: direntToLocalPath(dirent, { cwd: options.cwd }),
})),
options,
);
if (isExperimentalFsGlobEnabled && options.fs?.promises.glob) {
const params = { regularGlobs, ignoreGlobs, options, globs };
const nativeGlobResults = await getNativeGlobResults(params);
if (nativeGlobResults) return nativeGlobResults;
}
/** @type {RegExp[]} */
@ -390,7 +414,6 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
const isRootPath = isRootGlob(startPath);
const cwd = isRootPath ? '/' : options.cwd;
const fullStartPath = path.join(cwd, startPath);
try {
const allDirEntsRelativeToCwd = await getAllDirentsRelativeToCwd(fullStartPath, {
cwd,

View file

@ -40,8 +40,6 @@ async function runOptimisedGlobAndCheckGlobbyParity(patterns, options) {
);
}
console.debug({ optimisedGlobResult, globbyResult });
expect(optimisedGlobResult).to.deep.equal(globbyResult);
return optimisedGlobResult;
@ -74,6 +72,7 @@ function runSuiteForOptimisedGlob() {
'/fakeFs/my/folder/lvl1/lvl2/lvl3/some/anotherFile.d.ts': 'content',
'/fakeFs/my/.hiddenFile.js': 'content',
'/fakeFs/my/.hiddenFolder/file.js': 'content',
};
mockFs(fakeFs);
});
@ -275,9 +274,17 @@ function runSuiteForOptimisedGlob() {
});
it('"dot" allows hidden files" ', async () => {
const files = await runOptimisedGlobAndCheckGlobbyParity('*/*', { ...testCfg, dot: true });
const files = await runOptimisedGlobAndCheckGlobbyParity('*/*', {
...testCfg,
dot: true,
});
expect(files).to.deep.equal(['my/.hiddenFile.js']);
const files2 = await runOptimisedGlobAndCheckGlobbyParity('*/*/*', {
...testCfg,
dot: true,
});
expect(files2).to.deep.equal(['my/.hiddenFolder/file.js']);
});
it('"ignore" filters out files" ', async () => {
@ -335,6 +342,15 @@ function runSuiteForOptimisedGlob() {
// );
// expect(files3).to.deep.equal(['/fakeFs/my/folder/lvl1/some/file.js']);
});
it('starts from hidden folders', async () => {
const files = await runOptimisedGlobAndCheckGlobbyParity('**/*', {
...testCfg,
cwd: '/fakeFs/my/.hiddenFolder',
dot: true,
});
expect(files).to.deep.equal(['file.js']);
});
});
});
}