fix(providence): normalization of native glob dirent.parentPath for optimisedGlob
This commit is contained in:
parent
2d4fb0ecdb
commit
6f3137c963
3 changed files with 85 additions and 41 deletions
5
.changeset/rude-tips-smash.md
Normal file
5
.changeset/rude-tips-smash.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'providence-analytics': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix normalization of native glob dirent.parentPath for optimisedGlob
|
||||||
|
|
@ -144,7 +144,13 @@ function isRootGlob(glob) {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function direntToLocalPath(dirent, { cwd }) {
|
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(
|
return toPosixPath(path.join(folder, dirent.name)).replace(
|
||||||
new RegExp(`^${toPosixPath(cwd)}/`),
|
new RegExp(`^${toPosixPath(cwd)}/`),
|
||||||
'',
|
'',
|
||||||
|
|
@ -270,7 +276,7 @@ const getAllDirentsRelativeToCwd = memoize(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|string[]} globOrGlobs
|
* @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[]>}
|
* @returns {Promise<string[]|DirentWithPath[]>}
|
||||||
*/
|
*/
|
||||||
async function nativeGlob(globOrGlobs, { fs, cwd, exclude, stats }) {
|
async function nativeGlob(globOrGlobs, { fs, cwd, exclude, stats }) {
|
||||||
|
|
@ -288,6 +294,54 @@ async function nativeGlob(globOrGlobs, { fs, cwd, exclude, stats }) {
|
||||||
return results;
|
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.
|
* 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.
|
* 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 globs = toUniqueArray([...regularGlobs, ...ignoreGlobs]);
|
||||||
|
|
||||||
const optionsNotSupportedByNativeGlob = ['onlyDirectories', 'dot'];
|
if (isExperimentalFsGlobEnabled && options.fs?.promises.glob) {
|
||||||
const hasGlobWithFullPath = globs.some(isRootGlob);
|
const params = { regularGlobs, ignoreGlobs, options, globs };
|
||||||
const doesConfigAllowNative =
|
const nativeGlobResults = await getNativeGlobResults(params);
|
||||||
!optionsNotSupportedByNativeGlob.some(opt => options[opt]) && !hasGlobWithFullPath;
|
if (nativeGlobResults) return nativeGlobResults;
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {RegExp[]} */
|
/** @type {RegExp[]} */
|
||||||
|
|
@ -390,7 +414,6 @@ export async function optimisedGlob(globOrGlobs, providedOptions = {}) {
|
||||||
const isRootPath = isRootGlob(startPath);
|
const isRootPath = isRootGlob(startPath);
|
||||||
const cwd = isRootPath ? '/' : options.cwd;
|
const cwd = isRootPath ? '/' : options.cwd;
|
||||||
const fullStartPath = path.join(cwd, startPath);
|
const fullStartPath = path.join(cwd, startPath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const allDirEntsRelativeToCwd = await getAllDirentsRelativeToCwd(fullStartPath, {
|
const allDirEntsRelativeToCwd = await getAllDirentsRelativeToCwd(fullStartPath, {
|
||||||
cwd,
|
cwd,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@ 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;
|
||||||
|
|
@ -74,6 +72,7 @@ function runSuiteForOptimisedGlob() {
|
||||||
'/fakeFs/my/folder/lvl1/lvl2/lvl3/some/anotherFile.d.ts': 'content',
|
'/fakeFs/my/folder/lvl1/lvl2/lvl3/some/anotherFile.d.ts': 'content',
|
||||||
|
|
||||||
'/fakeFs/my/.hiddenFile.js': 'content',
|
'/fakeFs/my/.hiddenFile.js': 'content',
|
||||||
|
'/fakeFs/my/.hiddenFolder/file.js': 'content',
|
||||||
};
|
};
|
||||||
mockFs(fakeFs);
|
mockFs(fakeFs);
|
||||||
});
|
});
|
||||||
|
|
@ -275,9 +274,17 @@ function runSuiteForOptimisedGlob() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('"dot" allows hidden files" ', async () => {
|
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']);
|
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 () => {
|
it('"ignore" filters out files" ', async () => {
|
||||||
|
|
@ -335,6 +342,15 @@ function runSuiteForOptimisedGlob() {
|
||||||
// );
|
// );
|
||||||
// expect(files3).to.deep.equal(['/fakeFs/my/folder/lvl1/some/file.js']);
|
// 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']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue