-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fs: readdir optionally returning type information #22020
Changes from all commits
9b7b4d8
78880d5
a0bfbb0
3f920c7
49c7e62
b3c4ebc
019f025
e0a1191
c499a00
f01ed43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,8 @@ const { | |
const { isUint8Array } = require('internal/util/types'); | ||
const pathModule = require('path'); | ||
const util = require('util'); | ||
const kType = Symbol('type'); | ||
const kStats = Symbol('stats'); | ||
|
||
const { | ||
O_APPEND, | ||
|
@@ -31,24 +33,135 @@ const { | |
S_IFREG, | ||
S_IFSOCK, | ||
UV_FS_SYMLINK_DIR, | ||
UV_FS_SYMLINK_JUNCTION | ||
UV_FS_SYMLINK_JUNCTION, | ||
UV_DIRENT_UNKNOWN, | ||
UV_DIRENT_FILE, | ||
UV_DIRENT_DIR, | ||
UV_DIRENT_LINK, | ||
UV_DIRENT_FIFO, | ||
UV_DIRENT_SOCKET, | ||
UV_DIRENT_CHAR, | ||
UV_DIRENT_BLOCK | ||
} = process.binding('constants').fs; | ||
|
||
const isWindows = process.platform === 'win32'; | ||
|
||
let fs; | ||
function lazyLoadFs() { | ||
if (!fs) { | ||
fs = require('fs'); | ||
} | ||
return fs; | ||
} | ||
|
||
function assertEncoding(encoding) { | ||
if (encoding && !Buffer.isEncoding(encoding)) { | ||
throw new ERR_INVALID_OPT_VALUE_ENCODING(encoding); | ||
} | ||
} | ||
|
||
class Dirent { | ||
constructor(name, type) { | ||
this.name = name; | ||
this[kType] = type; | ||
} | ||
|
||
isDirectory() { | ||
return this[kType] === UV_DIRENT_DIR; | ||
} | ||
|
||
isFile() { | ||
return this[kType] === UV_DIRENT_FILE; | ||
} | ||
|
||
isBlockDevice() { | ||
return this[kType] === UV_DIRENT_BLOCK; | ||
} | ||
|
||
isCharacterDevice() { | ||
return this[kType] === UV_DIRENT_CHAR; | ||
} | ||
|
||
isSymbolicLink() { | ||
return this[kType] === UV_DIRENT_LINK; | ||
} | ||
|
||
isFIFO() { | ||
return this[kType] === UV_DIRENT_FIFO; | ||
} | ||
|
||
isSocket() { | ||
return this[kType] === UV_DIRENT_SOCKET; | ||
} | ||
} | ||
|
||
class DirentFromStats extends Dirent { | ||
constructor(name, stats) { | ||
super(name, null); | ||
this[kStats] = stats; | ||
} | ||
} | ||
|
||
for (const name of Reflect.ownKeys(Dirent.prototype)) { | ||
if (name === 'constructor') { | ||
continue; | ||
} | ||
DirentFromStats.prototype[name] = function() { | ||
return this[kStats][name](); | ||
}; | ||
} | ||
|
||
function copyObject(source) { | ||
var target = {}; | ||
for (var key in source) | ||
target[key] = source[key]; | ||
return target; | ||
} | ||
|
||
function getDirents(path, [names, types], callback) { | ||
var i; | ||
if (typeof callback == 'function') { | ||
const len = names.length; | ||
let toFinish = 0; | ||
for (i = 0; i < len; i++) { | ||
const type = types[i]; | ||
if (type === UV_DIRENT_UNKNOWN) { | ||
const name = names[i]; | ||
const idx = i; | ||
toFinish++; | ||
lazyLoadFs().stat(pathModule.resolve(path, name), (err, stats) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, it’s a surprise for me. If I understand correctly, it says here that if we do not know what type of current entry – call But what if I don't want and expect such behavior? For example, I want to collect unknown entries and do something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure we want to have this behavior? At a minimum, for me, DirEntry is associated with entry information, including information that the type is unknown for the current entry. It's confusing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This behavior was specifically asked for by @addaleax in this comment: #22020 (comment)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, in this case, the But, unfortunately, I still don't understand why the low-level tool has an implicit action. For example, I intended to make additional logic for unknown-entries in the package:
In the current situation we will receive the P.S.: That's interesting, can the same system provide a type for some entries, and for some not. ❤️ In any case, thank you for your work! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's an mistake on my part. Whoops! I'll add it in.
The intent (I think, based on the comment by @addaleax) is to actually have directory entry type information in as many cases as possible, and not depend on the user code to perform an extra action to get the information they asked for in the first place in the event of an easily recoverable bad case (i.e.
Perhaps a future optimization PR could cache the results of the first
Excellent question! To be honest, I'm not really sure what the exhaustive list of situations is, in which There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mrmlnc Sorry I looked over my code again and it seems that my intention was actually to remove the That being said, if you do find yourself in a situation where you want to know whether Sorry for the confusion! It's getting late here (UTC-7), haha. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, I agree with the current implementation. Thanks! |
||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
names[idx] = new DirentFromStats(name, stats); | ||
if (--toFinish === 0) { | ||
callback(null, names); | ||
} | ||
}); | ||
} else { | ||
names[i] = new Dirent(names[i], types[i]); | ||
} | ||
} | ||
if (toFinish === 0) { | ||
callback(null, names); | ||
} | ||
} else { | ||
const len = names.length; | ||
for (i = 0; i < len; i++) { | ||
const type = types[i]; | ||
if (type === UV_DIRENT_UNKNOWN) { | ||
const name = names[i]; | ||
const stats = lazyLoadFs().statSync(pathModule.resolve(path, name)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe that was asked before (I didn't read through all comments): why does this use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this use |
||
names[i] = new DirentFromStats(name, stats); | ||
} else { | ||
names[i] = new Dirent(names[i], types[i]); | ||
} | ||
} | ||
return names; | ||
} | ||
} | ||
|
||
function getOptions(options, defaultOptions) { | ||
if (options === null || options === undefined || | ||
typeof options === 'function') { | ||
|
@@ -342,6 +455,8 @@ function validatePath(path, propName = 'path') { | |
module.exports = { | ||
assertEncoding, | ||
copyObject, | ||
Dirent, | ||
getDirents, | ||
getOptions, | ||
nullCheck, | ||
preprocessSymlinkDestination, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we verify that
fs
isn't already loaded before this. 👆There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
internal/fs/utils
is required byfs
, so the dependency is circular. That's why lazily loading it is necessary.