Apply each callback in a list to the provided arguments. Callbacks can throw without preventing other callbacks from being applied.
function _applyAllCallbacks(callbacks, args) {
if (callbacks.length > 0) {
var callback = callbacks.pop();
try {
callback.apply(undefined, args);
} finally {
_applyAllCallbacks(callbacks, args);
}
}
}
Model for a file system Directory.
This class should not be instantiated directly. Use FileSystem.getDirectoryForPath, FileSystem.resolve, or Directory.getContents to create an instance of this class.
Note: Directory.fullPath always has a trailing slash.
See the FileSystem class for more details.
function Directory(fullPath, fileSystem) {
this._isDirectory = true;
FileSystemEntry.call(this, fullPath, fileSystem);
}
Directory.prototype = Object.create(FileSystemEntry.prototype);
Directory.prototype.constructor = Directory;
Directory.prototype.parentClass = FileSystemEntry.prototype;
The contents of this directory. This "private" property is used by FileSystem.
Directory.prototype._contents = null;
Clear any cached data for this directory. By default, we clear the contents of immediate children as well, because in some cases file watchers fail provide precise change notifications. (Sometimes, like after a "git checkout", they just report that some directory has changed when in fact many of the file within the directory have changed.
Directory.prototype._clearCachedData = function (preserveImmediateChildren) {
FileSystemEntry.prototype._clearCachedData.apply(this);
if (!preserveImmediateChildren) {
if (this._contents) {
this._contents.forEach(function (child) {
child._clearCachedData(true);
});
} else {
// No cached _contents, but child entries may still exist.
// Scan the full index to catch all of them.
var dirPath = this.fullPath;
this._fileSystem._index.visitAll(function (entry) {
if (entry.parentPath === dirPath) {
entry._clearCachedData(true);
}
});
}
}
this._contents = undefined;
this._contentsStats = undefined;
this._contentsStatsErrors = undefined;
};
Create a directory
Directory.prototype.create = function (callback) {
callback = callback || function () {};
// Block external change events until after the write has finished
this._fileSystem._beginChange();
this._impl.mkdir(this._path, function (err, stat) {
if (err) {
this._clearCachedData();
try {
callback(err);
return;
} finally {
// Unblock external change events
this._fileSystem._endChange();
}
}
var parent = this._fileSystem.getDirectoryForPath(this.parentPath);
// Update internal filesystem state
if (this._isWatched()) {
this._stat = stat;
}
this._fileSystem._handleDirectoryChange(parent, function (added, removed) {
try {
callback(null, stat);
} finally {
if (parent._isWatched()) {
this._fileSystem._fireChangeEvent(parent, added, removed);
}
// Unblock external change events
this._fileSystem._endChange();
}
}.bind(this));
}.bind(this));
};
// Export this class
module.exports = Directory;
});
Read the contents of a Directory. If this Directory is under a watch root, the listing will exclude any items filtered out by the watch root's filter function.
Directory.prototype.getContents = function (callback) {
if (this._contentsCallbacks) {
// There is already a pending call for this directory's contents.
// Push the new callback onto the stack and return.
this._contentsCallbacks.push(callback);
return;
}
// Return cached contents if the directory is watched
if (this._contents) {
callback(null, this._contents, this._contentsStats, this._contentsStatsErrors);
return;
}
this._contentsCallbacks = [callback];
this._impl.readdir(this.fullPath, function (err, names, stats) {
var contents = [],
contentsStats = [],
contentsStatsErrors;
if (err) {
this._clearCachedData();
} else {
// Use the "relaxed" parameter to _isWatched because it's OK to
// cache data even while watchers are still starting up
var watched = this._isWatched(true);
names.forEach(function (name, index) {
var entryPath = this.fullPath + name;
var entryStats = stats[index];
if (this._fileSystem._indexFilter(entryPath, name, entryStats)) {
var entry;
// Note: not all entries necessarily have associated stats.
if (typeof entryStats === "string") {
// entryStats is an error string
if (contentsStatsErrors === undefined) {
contentsStatsErrors = {};
}
contentsStatsErrors[entryPath] = entryStats;
} else {
// entryStats is a FileSystemStats object
if (entryStats.isFile) {
entry = this._fileSystem.getFileForPath(entryPath);
} else {
entry = this._fileSystem.getDirectoryForPath(entryPath);
}
if (watched) {
entry._stat = entryStats;
}
contents.push(entry);
contentsStats.push(entryStats);
}
}
}, this);
if (watched) {
this._contents = contents;
this._contentsStats = contentsStats;
this._contentsStatsErrors = contentsStatsErrors;
}
}
// Reset the callback list before we begin calling back so that
// synchronous reentrant calls are handled correctly.
var currentCallbacks = this._contentsCallbacks;
this._contentsCallbacks = null;
// Invoke all saved callbacks
var callbackArgs = [err, contents, contentsStats, contentsStatsErrors];
_applyAllCallbacks(currentCallbacks, callbackArgs);
}.bind(this));
};