зеркало из https://github.com/electron/electron.git
fix: support `withFileTypes` in `fs.{readdir|readdirSync}` (#41627)
fix: support withFileTypes in fs.{readdir|readdirSync}
This commit is contained in:
Родитель
dd3fd78e63
Коммит
040acaaf30
|
@ -89,18 +89,19 @@ const gid = process.getgid?.() ?? 0;
|
||||||
const fakeTime = new Date();
|
const fakeTime = new Date();
|
||||||
|
|
||||||
function getDirents (p: string, { 0: names, 1: types }: any[][]): Dirent[] {
|
function getDirents (p: string, { 0: names, 1: types }: any[][]): Dirent[] {
|
||||||
const info = splitPath(p);
|
for (let i = 0; i < names.length; i++) {
|
||||||
const len = names.length;
|
let type = types[i];
|
||||||
for (let i = 0; i < len; i++) {
|
const info = splitPath(path.join(p, names[i]));
|
||||||
if (info.isAsar) {
|
if (info.isAsar) {
|
||||||
const archive = getOrCreateArchive(info.asarPath);
|
const archive = getOrCreateArchive(info.asarPath);
|
||||||
const stats = archive!.stat(p);
|
if (!archive) continue;
|
||||||
|
const stats = archive.stat(info.filePath);
|
||||||
if (!stats) continue;
|
if (!stats) continue;
|
||||||
names[i] = getDirent(p, names[i], stats.type);
|
type = stats.type;
|
||||||
} else {
|
|
||||||
names[i] = getDirent(p, names[i], types[i]);
|
|
||||||
}
|
}
|
||||||
|
names[i] = getDirent(p, names[i], type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,6 +884,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
|
|
||||||
const pathInfo = splitPath(originalPath);
|
const pathInfo = splitPath(originalPath);
|
||||||
let queue: [string, string[]][] = [];
|
let queue: [string, string[]][] = [];
|
||||||
|
const withFileTypes = Boolean(options?.withFileTypes);
|
||||||
|
|
||||||
let initialItem = [];
|
let initialItem = [];
|
||||||
if (pathInfo.isAsar) {
|
if (pathInfo.isAsar) {
|
||||||
|
@ -890,19 +892,29 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
if (!archive) return result;
|
if (!archive) return result;
|
||||||
const files = archive.readdir(pathInfo.filePath);
|
const files = archive.readdir(pathInfo.filePath);
|
||||||
if (!files) return result;
|
if (!files) return result;
|
||||||
|
|
||||||
|
// If we're in an asar dir, we need to ensure the result is in the same format as the
|
||||||
|
// native call to readdir withFileTypes i.e. an array of arrays.
|
||||||
initialItem = files;
|
initialItem = files;
|
||||||
|
if (withFileTypes) {
|
||||||
|
initialItem = [
|
||||||
|
[...initialItem], initialItem.map((p: string) => {
|
||||||
|
return internalBinding('fs').internalModuleStat(path.join(originalPath, p));
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
initialItem = await binding.readdir(
|
initialItem = await binding.readdir(
|
||||||
path.toNamespacedPath(originalPath),
|
path.toNamespacedPath(originalPath),
|
||||||
options!.encoding,
|
options!.encoding,
|
||||||
!!options!.withFileTypes,
|
withFileTypes,
|
||||||
kUsePromises
|
kUsePromises
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
queue = [[originalPath, initialItem]];
|
queue = [[originalPath, initialItem]];
|
||||||
|
|
||||||
if (options?.withFileTypes) {
|
if (withFileTypes) {
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
// @ts-expect-error this is a valid array destructure assignment.
|
// @ts-expect-error this is a valid array destructure assignment.
|
||||||
const { 0: pathArg, 1: readDir } = queue.pop();
|
const { 0: pathArg, 1: readDir } = queue.pop();
|
||||||
|
@ -911,16 +923,22 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
if (dirent.isDirectory()) {
|
if (dirent.isDirectory()) {
|
||||||
const direntPath = path.join(pathArg, dirent.name);
|
const direntPath = path.join(pathArg, dirent.name);
|
||||||
const info = splitPath(direntPath);
|
const info = splitPath(direntPath);
|
||||||
let archive;
|
|
||||||
let readdirResult;
|
let readdirResult;
|
||||||
if (info.isAsar) {
|
if (info.isAsar) {
|
||||||
archive = getOrCreateArchive(info.asarPath);
|
const archive = getOrCreateArchive(info.asarPath);
|
||||||
if (!archive) continue;
|
if (!archive) continue;
|
||||||
readdirResult = archive.readdir(info.filePath);
|
const files = archive.readdir(info.filePath);
|
||||||
|
if (!files) continue;
|
||||||
|
|
||||||
|
readdirResult = [
|
||||||
|
[...files], files.map((p: string) => {
|
||||||
|
return internalBinding('fs').internalModuleStat(path.join(direntPath, p));
|
||||||
|
})
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
readdirResult = await binding.readdir(
|
readdirResult = await binding.readdir(
|
||||||
direntPath,
|
direntPath,
|
||||||
options.encoding,
|
options!.encoding,
|
||||||
true,
|
true,
|
||||||
kUsePromises
|
kUsePromises
|
||||||
);
|
);
|
||||||
|
@ -973,15 +991,24 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
|
|
||||||
function read (pathArg: string) {
|
function read (pathArg: string) {
|
||||||
let readdirResult;
|
let readdirResult;
|
||||||
const pathInfo = splitPath(pathArg);
|
|
||||||
|
|
||||||
let archive;
|
const pathInfo = splitPath(pathArg);
|
||||||
if (pathInfo.isAsar) {
|
if (pathInfo.isAsar) {
|
||||||
const { asarPath, filePath } = pathInfo;
|
const { asarPath, filePath } = pathInfo;
|
||||||
archive = getOrCreateArchive(asarPath);
|
const archive = getOrCreateArchive(asarPath);
|
||||||
if (!archive) return;
|
if (!archive) return;
|
||||||
|
|
||||||
readdirResult = archive.readdir(filePath);
|
readdirResult = archive.readdir(filePath);
|
||||||
|
if (!readdirResult) return;
|
||||||
|
// If we're in an asar dir, we need to ensure the result is in the same format as the
|
||||||
|
// native call to readdir withFileTypes i.e. an array of arrays.
|
||||||
|
if (withFileTypes) {
|
||||||
|
readdirResult = [
|
||||||
|
[...readdirResult], readdirResult.map((p: string) => {
|
||||||
|
return internalBinding('fs').internalModuleStat(path.join(pathArg, p));
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
readdirResult = binding.readdir(
|
readdirResult = binding.readdir(
|
||||||
path.toNamespacedPath(pathArg),
|
path.toNamespacedPath(pathArg),
|
||||||
|
@ -993,21 +1020,22 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
if (readdirResult === undefined) return;
|
if (readdirResult === undefined) return;
|
||||||
|
|
||||||
if (withFileTypes) {
|
if (withFileTypes) {
|
||||||
// Calling `readdir` with `withFileTypes=true`, the result is an array of arrays.
|
|
||||||
// The first array is the names, and the second array is the types.
|
|
||||||
// They are guaranteed to be the same length; hence, setting `length` to the length
|
|
||||||
// of the first array within the result.
|
|
||||||
const length = readdirResult[0].length;
|
const length = readdirResult[0].length;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
let dirent;
|
const resultPath = path.join(pathArg, readdirResult[0][i]);
|
||||||
if (pathInfo.isAsar) {
|
const info = splitPath(resultPath);
|
||||||
const stats = archive!.stat(pathArg);
|
|
||||||
|
let type = readdirResult[1][i];
|
||||||
|
if (info.isAsar) {
|
||||||
|
const archive = getOrCreateArchive(info.asarPath);
|
||||||
|
if (!archive) return;
|
||||||
|
const stats = archive.stat(info.filePath);
|
||||||
if (!stats) continue;
|
if (!stats) continue;
|
||||||
dirent = getDirent(pathArg, readdirResult[0][i], stats.type);
|
type = stats.type;
|
||||||
} else {
|
|
||||||
dirent = getDirent(pathArg, readdirResult[0][i], readdirResult[1][i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dirent = getDirent(pathArg, readdirResult[0][i], type);
|
||||||
|
|
||||||
readdirResults.push(dirent);
|
readdirResults.push(dirent);
|
||||||
if (dirent.isDirectory()) {
|
if (dirent.isDirectory()) {
|
||||||
pathsQueue.push(path.join(dirent.path, dirent.name));
|
pathsQueue.push(path.join(dirent.path, dirent.name));
|
||||||
|
@ -1018,12 +1046,9 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||||
const resultPath = path.join(pathArg, readdirResult[i]);
|
const resultPath = path.join(pathArg, readdirResult[i]);
|
||||||
const relativeResultPath = path.relative(basePath, resultPath);
|
const relativeResultPath = path.relative(basePath, resultPath);
|
||||||
const stat = internalBinding('fs').internalModuleStat(resultPath);
|
const stat = internalBinding('fs').internalModuleStat(resultPath);
|
||||||
readdirResults.push(relativeResultPath);
|
|
||||||
|
|
||||||
// 1 indicates directory
|
readdirResults.push(relativeResultPath);
|
||||||
if (stat === 1) {
|
if (stat === 1) pathsQueue.push(resultPath);
|
||||||
pathsQueue.push(resultPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -898,9 +898,28 @@ describe('asar package', function () {
|
||||||
expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']);
|
expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']);
|
||||||
});
|
});
|
||||||
|
|
||||||
itremote('supports recursive readdirSync', async () => {
|
itremote('supports recursive readdirSync withFileTypes', () => {
|
||||||
const dir = path.join(fixtures, 'recursive-asar');
|
const dir = path.join(fixtures, 'recursive-asar');
|
||||||
const files = await fs.readdirSync(dir, { recursive: true });
|
const files = fs.readdirSync(dir, { recursive: true, withFileTypes: true });
|
||||||
|
|
||||||
|
expect(files).to.have.length(24);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
expect(file).to.be.an.instanceOf(fs.Dirent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = files.map((a: any) => a.name);
|
||||||
|
expect(paths).to.have.members([
|
||||||
|
'a.asar', 'nested', 'test.txt', 'dir1', 'dir2', 'dir3',
|
||||||
|
'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js',
|
||||||
|
'hello.txt', 'file1', 'file2', 'file3', 'link1', 'link2',
|
||||||
|
'file1', 'file2', 'file3', 'file1', 'file2', 'file3'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
itremote('supports recursive readdirSync', () => {
|
||||||
|
const dir = path.join(fixtures, 'recursive-asar');
|
||||||
|
const files = fs.readdirSync(dir, { recursive: true });
|
||||||
expect(files).to.have.members([
|
expect(files).to.have.members([
|
||||||
'a.asar',
|
'a.asar',
|
||||||
'nested',
|
'nested',
|
||||||
|
@ -1007,6 +1026,25 @@ describe('asar package', function () {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itremote('supports readdir withFileTypes', async () => {
|
||||||
|
const dir = path.join(fixtures, 'recursive-asar');
|
||||||
|
const files = await promisify(fs.readdir)(dir, { recursive: true, withFileTypes: true });
|
||||||
|
|
||||||
|
expect(files).to.have.length(24);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
expect(file).to.be.an.instanceOf(fs.Dirent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = files.map((a: any) => a.name);
|
||||||
|
expect(paths).to.have.members([
|
||||||
|
'a.asar', 'nested', 'test.txt', 'dir1', 'dir2', 'dir3',
|
||||||
|
'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js',
|
||||||
|
'hello.txt', 'file1', 'file2', 'file3', 'link1', 'link2',
|
||||||
|
'file1', 'file2', 'file3', 'file1', 'file2', 'file3'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
itremote('supports withFileTypes', async () => {
|
itremote('supports withFileTypes', async () => {
|
||||||
const p = path.join(asarDir, 'a.asar');
|
const p = path.join(asarDir, 'a.asar');
|
||||||
|
|
||||||
|
@ -1102,6 +1140,25 @@ describe('asar package', function () {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itremote('supports readdir withFileTypes', async () => {
|
||||||
|
const dir = path.join(fixtures, 'recursive-asar');
|
||||||
|
const files = await fs.promises.readdir(dir, { recursive: true, withFileTypes: true });
|
||||||
|
|
||||||
|
expect(files).to.have.length(24);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
expect(file).to.be.an.instanceOf(fs.Dirent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = files.map((a: any) => a.name);
|
||||||
|
expect(paths).to.have.members([
|
||||||
|
'a.asar', 'nested', 'test.txt', 'dir1', 'dir2', 'dir3',
|
||||||
|
'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js',
|
||||||
|
'hello.txt', 'file1', 'file2', 'file3', 'link1', 'link2',
|
||||||
|
'file1', 'file2', 'file3', 'file1', 'file2', 'file3'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
itremote('supports withFileTypes', async function () {
|
itremote('supports withFileTypes', async function () {
|
||||||
const p = path.join(asarDir, 'a.asar');
|
const p = path.join(asarDir, 'a.asar');
|
||||||
const dirs = await fs.promises.readdir(p, { withFileTypes: true });
|
const dirs = await fs.promises.readdir(p, { withFileTypes: true });
|
||||||
|
|
Загрузка…
Ссылка в новой задаче