Beta 7 with some repo names changed.
This commit is contained in:
1 parent
0f7b40cca7
commit
963c19493a
3408 files changed
+462217
-15516
No files matched your search
+1516
File diff suppressed because it is too large.
Load diff
+14
@@ -0,0 +1,14 @@
|
||||
exports.from =
|
||||
Buffer.from ||
|
||||
function(value, encoding) {
|
||||
if (encoding) {
|
||||
return new Buffer(value, encoding);
|
||||
}
|
||||
return new Buffer(value);
|
||||
};
|
||||
|
||||
exports.alloc =
|
||||
Buffer.alloc ||
|
||||
function(size) {
|
||||
return new Buffer(size);
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
const realBinding = process.binding('fs');
|
||||
let storedBinding;
|
||||
|
||||
/**
|
||||
* Perform action, bypassing mock FS
|
||||
* @example
|
||||
* // This file exists on the real FS, not on the mocked FS
|
||||
* const filePath = '/path/file.json';
|
||||
* const data = mock.bypass(() => fs.readFileSync(filePath, 'utf-8'));
|
||||
*/
|
||||
exports = module.exports = function bypass(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error(`Must provide a function to perform for mock.bypass()`);
|
||||
}
|
||||
|
||||
disable();
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = fn();
|
||||
} finally {
|
||||
if (result && typeof result.finally === 'function') {
|
||||
result.finally(enable);
|
||||
} else {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporarily disable Mocked FS
|
||||
*/
|
||||
function disable() {
|
||||
if (realBinding._mockedBinding) {
|
||||
storedBinding = realBinding._mockedBinding;
|
||||
delete realBinding._mockedBinding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Mocked FS after being disabled by disable()
|
||||
*/
|
||||
function enable() {
|
||||
if (storedBinding) {
|
||||
realBinding._mockedBinding = storedBinding;
|
||||
storedBinding = undefined;
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
const constants = require('constants');
|
||||
|
||||
/**
|
||||
* Create a new file descriptor.
|
||||
* @param {number} flags Flags.
|
||||
* @constructor
|
||||
*/
|
||||
function FileDescriptor(flags) {
|
||||
/**
|
||||
* Flags.
|
||||
* @type {number}
|
||||
*/
|
||||
this._flags = flags;
|
||||
|
||||
/**
|
||||
* File system item.
|
||||
* @type {Item}
|
||||
*/
|
||||
this._item = null;
|
||||
|
||||
/**
|
||||
* Current file position.
|
||||
* @type {number}
|
||||
*/
|
||||
this._position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the item.
|
||||
* @param {Item} item File system item.
|
||||
*/
|
||||
FileDescriptor.prototype.setItem = function(item) {
|
||||
this._item = item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the item.
|
||||
* @return {Item} File system item.
|
||||
*/
|
||||
FileDescriptor.prototype.getItem = function() {
|
||||
return this._item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current file position.
|
||||
* @return {number} File position.
|
||||
*/
|
||||
FileDescriptor.prototype.getPosition = function() {
|
||||
return this._position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the current file position.
|
||||
* @param {number} position File position.
|
||||
*/
|
||||
FileDescriptor.prototype.setPosition = function(position) {
|
||||
this._position = position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened for appending.
|
||||
* @return {boolean} Opened for appending.
|
||||
*/
|
||||
FileDescriptor.prototype.isAppend = function() {
|
||||
return (this._flags & constants.O_APPEND) === constants.O_APPEND;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened for creation.
|
||||
* @return {boolean} Opened for creation.
|
||||
*/
|
||||
FileDescriptor.prototype.isCreate = function() {
|
||||
return (this._flags & constants.O_CREAT) === constants.O_CREAT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened for reading.
|
||||
* @return {boolean} Opened for reading.
|
||||
*/
|
||||
FileDescriptor.prototype.isRead = function() {
|
||||
return (this._flags & constants.O_WRONLY) !== constants.O_WRONLY;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened for writing.
|
||||
* @return {boolean} Opened for writing.
|
||||
*/
|
||||
FileDescriptor.prototype.isWrite = function() {
|
||||
return (
|
||||
(this._flags & constants.O_WRONLY) === constants.O_WRONLY ||
|
||||
(this._flags & constants.O_RDWR) === constants.O_RDWR
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened for truncating.
|
||||
* @return {boolean} Opened for truncating.
|
||||
*/
|
||||
FileDescriptor.prototype.isTruncate = function() {
|
||||
return (this._flags & constants.O_TRUNC) === constants.O_TRUNC;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if file opened with exclusive flag.
|
||||
* @return {boolean} Opened with exclusive.
|
||||
*/
|
||||
FileDescriptor.prototype.isExclusive = function() {
|
||||
return (this._flags & constants.O_EXCL) === constants.O_EXCL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
* @type {function()}
|
||||
*/
|
||||
exports = module.exports = FileDescriptor;
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
|
||||
const Item = require('./item');
|
||||
|
||||
const constants = require('constants');
|
||||
|
||||
/**
|
||||
* A directory.
|
||||
* @constructor
|
||||
*/
|
||||
function Directory() {
|
||||
Item.call(this);
|
||||
|
||||
/**
|
||||
* Items in this directory.
|
||||
* @type {Object.<string, Item>}
|
||||
*/
|
||||
this._items = {};
|
||||
|
||||
/**
|
||||
* Permissions.
|
||||
*/
|
||||
this._mode = 511; // 0777
|
||||
}
|
||||
util.inherits(Directory, Item);
|
||||
|
||||
/**
|
||||
* Add an item to the directory.
|
||||
* @param {string} name The name to give the item.
|
||||
* @param {Item} item The item to add.
|
||||
* @return {Item} The added item.
|
||||
*/
|
||||
Directory.prototype.addItem = function(name, item) {
|
||||
if (this._items.hasOwnProperty(name)) {
|
||||
throw new Error('Item with the same name already exists: ' + name);
|
||||
}
|
||||
this._items[name] = item;
|
||||
++item.links;
|
||||
if (item instanceof Directory) {
|
||||
// for '.' entry
|
||||
++item.links;
|
||||
// for subdirectory
|
||||
++this.links;
|
||||
}
|
||||
this.setMTime(new Date());
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a named item.
|
||||
* @param {string} name Item name.
|
||||
* @return {Item} The named item (or null if none).
|
||||
*/
|
||||
Directory.prototype.getItem = function(name) {
|
||||
let item = null;
|
||||
if (this._items.hasOwnProperty(name)) {
|
||||
item = this._items[name];
|
||||
}
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an item.
|
||||
* @param {string} name Name of item to remove.
|
||||
* @return {Item} The orphan item.
|
||||
*/
|
||||
Directory.prototype.removeItem = function(name) {
|
||||
if (!this._items.hasOwnProperty(name)) {
|
||||
throw new Error('Item does not exist in directory: ' + name);
|
||||
}
|
||||
const item = this._items[name];
|
||||
delete this._items[name];
|
||||
--item.links;
|
||||
if (item instanceof Directory) {
|
||||
// for '.' entry
|
||||
--item.links;
|
||||
// for subdirectory
|
||||
--this.links;
|
||||
}
|
||||
this.setMTime(new Date());
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of item names in this directory.
|
||||
* @return {Array.<string>} Item names.
|
||||
*/
|
||||
Directory.prototype.list = function() {
|
||||
return Object.keys(this._items).sort();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get directory stats.
|
||||
* @return {Object} Stats properties.
|
||||
*/
|
||||
Directory.prototype.getStats = function() {
|
||||
const stats = Item.prototype.getStats.call(this);
|
||||
stats.mode = this.getMode() | constants.S_IFDIR;
|
||||
stats.size = 1;
|
||||
stats.blocks = 1;
|
||||
return stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
* @type {function()}
|
||||
*/
|
||||
exports = module.exports = Directory;
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
const uvBinding = process.binding('uv');
|
||||
/**
|
||||
* Error codes from libuv.
|
||||
* @enum {number}
|
||||
*/
|
||||
const codes = {};
|
||||
|
||||
if (uvBinding.errmap) {
|
||||
// nodejs v8+
|
||||
uvBinding.errmap.forEach(function(value, errno) {
|
||||
const code = value[0];
|
||||
const message = value[1];
|
||||
codes[code] = {errno: errno, message: message};
|
||||
});
|
||||
} else {
|
||||
// nodejs v4 and v6
|
||||
Object.keys(uvBinding).forEach(function(key) {
|
||||
if (key.startsWith('UV_')) {
|
||||
const code = key.slice(3);
|
||||
const errno = uvBinding[key];
|
||||
codes[code] = {errno: errno, message: key};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error.
|
||||
* @param {string} code Error code.
|
||||
* @param {string} path Path (optional).
|
||||
* @constructor
|
||||
*/
|
||||
function FSError(code, path) {
|
||||
if (!codes.hasOwnProperty(code)) {
|
||||
throw new Error('Programmer error, invalid error code: ' + code);
|
||||
}
|
||||
Error.call(this);
|
||||
const details = codes[code];
|
||||
let message = code + ', ' + details.message;
|
||||
if (path) {
|
||||
message += " '" + path + "'";
|
||||
}
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
this.errno = details.errno;
|
||||
if (path !== undefined) {
|
||||
this.path = path;
|
||||
}
|
||||
Error.captureStackTrace(this, FSError);
|
||||
}
|
||||
FSError.prototype = new Error();
|
||||
FSError.codes = codes;
|
||||
|
||||
/**
|
||||
* Error constructor.
|
||||
*/
|
||||
exports = module.exports = FSError;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
|
||||
const Item = require('./item');
|
||||
const bufferFrom = require('./buffer').from;
|
||||
const bufferAlloc = require('./buffer').alloc;
|
||||
|
||||
const EMPTY = bufferAlloc(0);
|
||||
const constants = require('constants');
|
||||
|
||||
/**
|
||||
* A directory.
|
||||
* @constructor
|
||||
*/
|
||||
function File() {
|
||||
Item.call(this);
|
||||
|
||||
/**
|
||||
* File content.
|
||||
* @type {Buffer}
|
||||
*/
|
||||
this._content = EMPTY;
|
||||
}
|
||||
util.inherits(File, Item);
|
||||
|
||||
/**
|
||||
* Get the file contents.
|
||||
* @return {Buffer} File contents.
|
||||
*/
|
||||
File.prototype.getContent = function() {
|
||||
this.setATime(new Date());
|
||||
return this._content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the file contents.
|
||||
* @param {string|Buffer} content File contents.
|
||||
*/
|
||||
File.prototype.setContent = function(content) {
|
||||
if (typeof content === 'string') {
|
||||
content = bufferFrom(content);
|
||||
} else if (!Buffer.isBuffer(content)) {
|
||||
throw new Error('File content must be a string or buffer');
|
||||
}
|
||||
this._content = content;
|
||||
const now = Date.now();
|
||||
this.setCTime(new Date(now));
|
||||
this.setMTime(new Date(now));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get file stats.
|
||||
* @return {Object} Stats properties.
|
||||
*/
|
||||
File.prototype.getStats = function() {
|
||||
const size = this._content.length;
|
||||
const stats = Item.prototype.getStats.call(this);
|
||||
stats.mode = this.getMode() | constants.S_IFREG;
|
||||
stats.size = size;
|
||||
stats.blocks = Math.ceil(size / 512);
|
||||
return stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
* @type {function()}
|
||||
*/
|
||||
exports = module.exports = File;
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const Directory = require('./directory');
|
||||
const File = require('./file');
|
||||
const FSError = require('./error');
|
||||
const SymbolicLink = require('./symlink');
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
function toNamespacedPath(filePath) {
|
||||
return path.toNamespacedPath
|
||||
? path.toNamespacedPath(filePath)
|
||||
: path._makeLong(filePath);
|
||||
}
|
||||
|
||||
function getPathParts(filepath) {
|
||||
const parts = toNamespacedPath(path.resolve(filepath)).split(path.sep);
|
||||
parts.shift();
|
||||
if (isWindows) {
|
||||
// parts currently looks like ['', '?', 'c:', ...]
|
||||
parts.shift();
|
||||
const q = parts.shift(); // should be '?'
|
||||
const base = '\\\\' + q + '\\' + parts.shift().toLowerCase();
|
||||
parts.unshift(base);
|
||||
}
|
||||
if (parts[parts.length - 1] === '') {
|
||||
parts.pop();
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new file system.
|
||||
* @param {Object} options Any filesystem options.
|
||||
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
|
||||
* (defaults to `true`).
|
||||
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
|
||||
* (defaults to `true`).
|
||||
* @constructor
|
||||
*/
|
||||
function FileSystem(options) {
|
||||
options = options || {};
|
||||
|
||||
const createCwd = 'createCwd' in options ? options.createCwd : true;
|
||||
const createTmp = 'createTmp' in options ? options.createTmp : true;
|
||||
|
||||
const root = new Directory();
|
||||
|
||||
// populate with default directories
|
||||
const defaults = [];
|
||||
if (createCwd) {
|
||||
defaults.push(process.cwd());
|
||||
}
|
||||
|
||||
if (createTmp) {
|
||||
defaults.push((os.tmpdir && os.tmpdir()) || os.tmpDir());
|
||||
}
|
||||
|
||||
defaults.forEach(function(dir) {
|
||||
const parts = getPathParts(dir);
|
||||
let directory = root;
|
||||
for (let i = 0, ii = parts.length; i < ii; ++i) {
|
||||
const name = parts[i];
|
||||
const candidate = directory.getItem(name);
|
||||
if (!candidate) {
|
||||
directory = directory.addItem(name, new Directory());
|
||||
} else if (candidate instanceof Directory) {
|
||||
directory = candidate;
|
||||
} else {
|
||||
throw new Error('Failed to create directory: ' + dir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Root directory.
|
||||
* @type {Directory}
|
||||
*/
|
||||
this._root = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root directory.
|
||||
* @return {Directory} The root directory.
|
||||
*/
|
||||
FileSystem.prototype.getRoot = function() {
|
||||
return this._root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a file system item.
|
||||
* @param {string} filepath Path to item.
|
||||
* @return {Item} The item (or null if not found).
|
||||
*/
|
||||
FileSystem.prototype.getItem = function(filepath) {
|
||||
const parts = getPathParts(filepath);
|
||||
const currentParts = getPathParts(process.cwd());
|
||||
let item = this._root;
|
||||
let itemPath = '/';
|
||||
for (let i = 0, ii = parts.length; i < ii; ++i) {
|
||||
const name = parts[i];
|
||||
while (item instanceof SymbolicLink) {
|
||||
// Symbolic link being traversed as a directory --- If link targets
|
||||
// another symbolic link, resolve target's path relative to the original
|
||||
// link's target, otherwise relative to the current item.
|
||||
itemPath = path.resolve(path.dirname(itemPath), item.getPath());
|
||||
item = this.getItem(itemPath);
|
||||
}
|
||||
if (item) {
|
||||
if (item instanceof Directory && name !== currentParts[i]) {
|
||||
// make sure traversal is allowed
|
||||
// This fails for Windows directories which do not have execute permission, by default. It may be a good idea
|
||||
// to change this logic to windows-friendly. See notes in mock.createDirectoryInfoFromPaths()
|
||||
if (!item.canExecute()) {
|
||||
throw new FSError('EACCES', filepath);
|
||||
}
|
||||
}
|
||||
if (item instanceof File) {
|
||||
throw new FSError('ENOTDIR', filepath);
|
||||
}
|
||||
item = item.getItem(name);
|
||||
}
|
||||
if (!item) {
|
||||
break;
|
||||
}
|
||||
itemPath = path.resolve(itemPath, name);
|
||||
}
|
||||
return item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate a directory with an item.
|
||||
* @param {Directory} directory The directory to populate.
|
||||
* @param {string} name The name of the item.
|
||||
* @param {string|Buffer|function|Object} obj Instructions for creating the
|
||||
* item.
|
||||
*/
|
||||
function populate(directory, name, obj) {
|
||||
let item;
|
||||
if (typeof obj === 'string' || Buffer.isBuffer(obj)) {
|
||||
// contents for a file
|
||||
item = new File();
|
||||
item.setContent(obj);
|
||||
} else if (typeof obj === 'function') {
|
||||
// item factory
|
||||
item = obj();
|
||||
} else if (typeof obj === 'object') {
|
||||
// directory with more to populate
|
||||
item = new Directory();
|
||||
for (const key in obj) {
|
||||
populate(item, key, obj[key]);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unsupported type: ' + typeof obj + ' of item ' + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special exception for redundant adding of empty directories.
|
||||
*/
|
||||
if (
|
||||
item instanceof Directory &&
|
||||
item.list().length === 0 &&
|
||||
directory.getItem(name) instanceof Directory
|
||||
) {
|
||||
// pass
|
||||
} else {
|
||||
directory.addItem(name, item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a mock file system.
|
||||
* @param {Object} paths Config object.
|
||||
* @param {Object} options Any filesystem options.
|
||||
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
|
||||
* (defaults to `true`).
|
||||
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
|
||||
* (defaults to `true`).
|
||||
* @return {FileSystem} Mock file system.
|
||||
*/
|
||||
FileSystem.create = function(paths, options) {
|
||||
const system = new FileSystem(options);
|
||||
|
||||
for (const filepath in paths) {
|
||||
const parts = getPathParts(filepath);
|
||||
let directory = system._root;
|
||||
for (let i = 0, ii = parts.length - 1; i < ii; ++i) {
|
||||
const name = parts[i];
|
||||
const candidate = directory.getItem(name);
|
||||
if (!candidate) {
|
||||
directory = directory.addItem(name, new Directory());
|
||||
} else if (candidate instanceof Directory) {
|
||||
directory = candidate;
|
||||
} else {
|
||||
throw new Error('Failed to create directory: ' + filepath);
|
||||
}
|
||||
}
|
||||
populate(directory, parts[parts.length - 1], paths[filepath]);
|
||||
}
|
||||
|
||||
return system;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a factory for new files.
|
||||
* @param {Object} config File config.
|
||||
* @return {function():File} Factory that creates a new file.
|
||||
*/
|
||||
FileSystem.file = function(config) {
|
||||
config = config || {};
|
||||
return function() {
|
||||
const file = new File();
|
||||
if (config.hasOwnProperty('content')) {
|
||||
file.setContent(config.content);
|
||||
}
|
||||
if (config.hasOwnProperty('mode')) {
|
||||
file.setMode(config.mode);
|
||||
} else {
|
||||
file.setMode(438); // 0666
|
||||
}
|
||||
if (config.hasOwnProperty('uid')) {
|
||||
file.setUid(config.uid);
|
||||
}
|
||||
if (config.hasOwnProperty('gid')) {
|
||||
file.setGid(config.gid);
|
||||
}
|
||||
if (config.hasOwnProperty('atime')) {
|
||||
file.setATime(config.atime);
|
||||
}
|
||||
if (config.hasOwnProperty('ctime')) {
|
||||
file.setCTime(config.ctime);
|
||||
}
|
||||
if (config.hasOwnProperty('mtime')) {
|
||||
file.setMTime(config.mtime);
|
||||
}
|
||||
if (config.hasOwnProperty('birthtime')) {
|
||||
file.setBirthtime(config.birthtime);
|
||||
}
|
||||
return file;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a factory for new symbolic links.
|
||||
* @param {Object} config File config.
|
||||
* @return {function():File} Factory that creates a new symbolic link.
|
||||
*/
|
||||
FileSystem.symlink = function(config) {
|
||||
config = config || {};
|
||||
return function() {
|
||||
const link = new SymbolicLink();
|
||||
if (config.hasOwnProperty('mode')) {
|
||||
link.setMode(config.mode);
|
||||
} else {
|
||||
link.setMode(438); // 0666
|
||||
}
|
||||
if (config.hasOwnProperty('uid')) {
|
||||
link.setUid(config.uid);
|
||||
}
|
||||
if (config.hasOwnProperty('gid')) {
|
||||
link.setGid(config.gid);
|
||||
}
|
||||
if (config.hasOwnProperty('path')) {
|
||||
link.setPath(config.path);
|
||||
} else {
|
||||
throw new Error('Missing "path" property');
|
||||
}
|
||||
if (config.hasOwnProperty('atime')) {
|
||||
link.setATime(config.atime);
|
||||
}
|
||||
if (config.hasOwnProperty('ctime')) {
|
||||
link.setCTime(config.ctime);
|
||||
}
|
||||
if (config.hasOwnProperty('mtime')) {
|
||||
link.setMTime(config.mtime);
|
||||
}
|
||||
if (config.hasOwnProperty('birthtime')) {
|
||||
link.setBirthtime(config.birthtime);
|
||||
}
|
||||
return link;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a factory for new directories.
|
||||
* @param {Object} config File config.
|
||||
* @return {function():Directory} Factory that creates a new directory.
|
||||
*/
|
||||
FileSystem.directory = function(config) {
|
||||
config = config || {};
|
||||
return function() {
|
||||
const dir = new Directory();
|
||||
if (config.hasOwnProperty('mode')) {
|
||||
dir.setMode(config.mode);
|
||||
}
|
||||
if (config.hasOwnProperty('uid')) {
|
||||
dir.setUid(config.uid);
|
||||
}
|
||||
if (config.hasOwnProperty('gid')) {
|
||||
dir.setGid(config.gid);
|
||||
}
|
||||
if (config.hasOwnProperty('items')) {
|
||||
for (const name in config.items) {
|
||||
populate(dir, name, config.items[name]);
|
||||
}
|
||||
}
|
||||
if (config.hasOwnProperty('atime')) {
|
||||
dir.setATime(config.atime);
|
||||
}
|
||||
if (config.hasOwnProperty('ctime')) {
|
||||
dir.setCTime(config.ctime);
|
||||
}
|
||||
if (config.hasOwnProperty('mtime')) {
|
||||
dir.setMTime(config.mtime);
|
||||
}
|
||||
if (config.hasOwnProperty('birthtime')) {
|
||||
dir.setBirthtime(config.birthtime);
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @type {function}
|
||||
*/
|
||||
exports = module.exports = FileSystem;
|
||||
exports.getPathParts = getPathParts;
|
||||
exports.toNamespacedPath = toNamespacedPath;
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
'use strict';
|
||||
|
||||
const Binding = require('./binding');
|
||||
const FSError = require('./error');
|
||||
const FileSystem = require('./filesystem');
|
||||
const realBinding = process.binding('fs');
|
||||
const path = require('path');
|
||||
const loader = require('./loader');
|
||||
const bypass = require('./bypass');
|
||||
const fs = require('fs');
|
||||
|
||||
const toNamespacedPath = FileSystem.toNamespacedPath;
|
||||
|
||||
const realProcessProps = {
|
||||
cwd: process.cwd,
|
||||
chdir: process.chdir
|
||||
};
|
||||
const realCreateWriteStream = fs.createWriteStream;
|
||||
const realStats = realBinding.Stats;
|
||||
const realStatWatcher = realBinding.StatWatcher;
|
||||
|
||||
/**
|
||||
* Pre-patch fs binding.
|
||||
* This allows mock-fs to work properly under nodejs v10+ readFile
|
||||
* As ReadFileContext nodejs v10+ implementation traps original binding methods:
|
||||
* const { FSReqWrap, close, read } = process.binding('fs');
|
||||
* Note this patch only solves issue for readFile, as the require of
|
||||
* ReadFileContext is delayed by readFile implementation.
|
||||
* if (!ReadFileContext) ReadFileContext = require('internal/fs/read_file_context')
|
||||
*
|
||||
* @param {string} key Property name.
|
||||
*/
|
||||
function patch(key) {
|
||||
const existingMethod = realBinding[key];
|
||||
realBinding[key] = function() {
|
||||
if (this._mockedBinding) {
|
||||
return this._mockedBinding[key].apply(this._mockedBinding, arguments);
|
||||
} else {
|
||||
return existingMethod.apply(this, arguments);
|
||||
}
|
||||
}.bind(realBinding);
|
||||
}
|
||||
|
||||
for (const key in Binding.prototype) {
|
||||
if (typeof realBinding[key] === 'function') {
|
||||
// Stats and StatWatcher are constructors
|
||||
if (key !== 'Stats' && key !== 'StatWatcher') {
|
||||
patch(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function overrideBinding(binding) {
|
||||
realBinding._mockedBinding = binding;
|
||||
}
|
||||
|
||||
function overrideProcess(cwd, chdir) {
|
||||
process.cwd = cwd;
|
||||
process.chdir = chdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Have to disable write stream _writev on nodejs v10+.
|
||||
*
|
||||
* nodejs v8 lib/fs.js
|
||||
* note binding.writeBuffers will use mock-fs patched writeBuffers.
|
||||
*
|
||||
* const binding = process.binding('fs');
|
||||
* function writev(fd, chunks, position, callback) {
|
||||
* // ...
|
||||
* binding.writeBuffers(fd, chunks, position, req);
|
||||
* }
|
||||
*
|
||||
* nodejs v10+ lib/internal/fs/streams.js
|
||||
* note it uses original writeBuffers, bypassed mock-fs patched writeBuffers.
|
||||
*
|
||||
* const {writeBuffers} = internalBinding('fs');
|
||||
* function writev(fd, chunks, position, callback) {
|
||||
* // ...
|
||||
* writeBuffers(fd, chunks, position, req);
|
||||
* }
|
||||
*
|
||||
* Luckily _writev is an optional method on Writeable stream implementation.
|
||||
* When _writev is missing, it will fall back to make multiple _write calls.
|
||||
*/
|
||||
function overrideCreateWriteStream() {
|
||||
fs.createWriteStream = function(path, options) {
|
||||
const output = realCreateWriteStream(path, options);
|
||||
// disable _writev, this will over shadow WriteStream.prototype._writev
|
||||
if (realBinding._mockedBinding) {
|
||||
output._writev = undefined;
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
function restoreBinding() {
|
||||
delete realBinding._mockedBinding;
|
||||
realBinding.Stats = realStats;
|
||||
realBinding.StatWatcher = realStatWatcher;
|
||||
}
|
||||
|
||||
function restoreProcess() {
|
||||
for (const key in realProcessProps) {
|
||||
process[key] = realProcessProps[key];
|
||||
}
|
||||
}
|
||||
|
||||
function restoreCreateWriteStream() {
|
||||
fs.createWriteStream = realCreateWriteStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap out the fs bindings for a mock file system.
|
||||
* @param {Object} config Mock file system configuration.
|
||||
* @param {Object} options Any filesystem options.
|
||||
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
|
||||
* (defaults to `true`).
|
||||
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
|
||||
* (defaults to `true`).
|
||||
*/
|
||||
exports = module.exports = function mock(config, options) {
|
||||
const system = FileSystem.create(config, options);
|
||||
const binding = new Binding(system);
|
||||
|
||||
overrideBinding(binding);
|
||||
|
||||
let currentPath = process.cwd();
|
||||
overrideProcess(
|
||||
function cwd() {
|
||||
if (realBinding._mockedBinding) {
|
||||
return currentPath;
|
||||
}
|
||||
return realProcessProps.cwd();
|
||||
},
|
||||
function chdir(directory) {
|
||||
if (realBinding._mockedBinding) {
|
||||
if (!binding.stat(toNamespacedPath(directory)).isDirectory()) {
|
||||
throw new FSError('ENOTDIR');
|
||||
}
|
||||
currentPath = path.resolve(currentPath, directory);
|
||||
} else {
|
||||
return realProcessProps.chdir(directory);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
overrideCreateWriteStream();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get hold of the mocked filesystem's 'root'
|
||||
* If fs hasn't currently been replaced, this will return an empty object
|
||||
*/
|
||||
exports.getMockRoot = function() {
|
||||
if (realBinding._mockedBinding) {
|
||||
return realBinding._mockedBinding.getSystem().getRoot();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Restore the fs bindings for the real file system.
|
||||
*/
|
||||
exports.restore = function() {
|
||||
restoreBinding();
|
||||
restoreProcess();
|
||||
restoreCreateWriteStream();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a file factory.
|
||||
*/
|
||||
exports.file = FileSystem.file;
|
||||
|
||||
/**
|
||||
* Create a directory factory.
|
||||
*/
|
||||
exports.directory = FileSystem.directory;
|
||||
|
||||
/**
|
||||
* Create a symbolic link factory.
|
||||
*/
|
||||
exports.symlink = FileSystem.symlink;
|
||||
|
||||
/**
|
||||
* Automatically maps specified paths (for use with `mock()`)
|
||||
*/
|
||||
exports.load = loader.load;
|
||||
|
||||
/**
|
||||
* Perform action, bypassing mock FS
|
||||
* @example
|
||||
* // This file exists on the real FS, not on the mocked FS
|
||||
* const filePath = '/path/file.json';
|
||||
* const data = mock.bypass(() => fs.readFileSync(filePath, 'utf-8'));
|
||||
*/
|
||||
exports.bypass = bypass;
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
'use strict';
|
||||
|
||||
let counter = 0;
|
||||
|
||||
/**
|
||||
* Permissions.
|
||||
* @enum {number}
|
||||
*/
|
||||
const permissions = {
|
||||
USER_READ: 256, // 0400
|
||||
USER_WRITE: 128, // 0200
|
||||
USER_EXEC: 64, // 0100
|
||||
GROUP_READ: 32, // 0040
|
||||
GROUP_WRITE: 16, // 0020
|
||||
GROUP_EXEC: 8, // 0010
|
||||
OTHER_READ: 4, // 0004
|
||||
OTHER_WRITE: 2, // 0002
|
||||
OTHER_EXEC: 1 // 0001
|
||||
};
|
||||
|
||||
function getUid() {
|
||||
// force NaN on windows.
|
||||
return process.getuid ? process.getuid() : NaN;
|
||||
}
|
||||
|
||||
function getGid() {
|
||||
// force NaN on windows.
|
||||
return process.getgid ? process.getgid() : NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* A filesystem item.
|
||||
* @constructor
|
||||
*/
|
||||
function Item() {
|
||||
const now = Date.now();
|
||||
|
||||
/**
|
||||
* Access time.
|
||||
* @type {Date}
|
||||
*/
|
||||
this._atime = new Date(now);
|
||||
|
||||
/**
|
||||
* Change time.
|
||||
* @type {Date}
|
||||
*/
|
||||
this._ctime = new Date(now);
|
||||
|
||||
/**
|
||||
* Birth time.
|
||||
* @type {Date}
|
||||
*/
|
||||
this._birthtime = new Date(now);
|
||||
|
||||
/**
|
||||
* Modification time.
|
||||
* @type {Date}
|
||||
*/
|
||||
this._mtime = new Date(now);
|
||||
|
||||
/**
|
||||
* Permissions.
|
||||
*/
|
||||
this._mode = 438; // 0666
|
||||
|
||||
/**
|
||||
* User id.
|
||||
* @type {number}
|
||||
*/
|
||||
this._uid = getUid();
|
||||
|
||||
/**
|
||||
* Group id.
|
||||
* @type {number}
|
||||
*/
|
||||
this._gid = getGid();
|
||||
|
||||
/**
|
||||
* Item number.
|
||||
* @type {number}
|
||||
*/
|
||||
this._id = ++counter;
|
||||
|
||||
/**
|
||||
* Number of links to this item.
|
||||
*/
|
||||
this.links = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add execute if read allowed
|
||||
* See notes in index.js -> mapping#addDir
|
||||
*/
|
||||
// prettier-ignore
|
||||
Item.fixWin32Permissions = mode =>
|
||||
(process.platform !== 'win32')
|
||||
? mode
|
||||
: mode |
|
||||
((mode & permissions.USER_READ) && permissions.USER_EXEC) |
|
||||
((mode & permissions.GROUP_READ) && permissions.GROUP_EXEC) |
|
||||
((mode & permissions.OTHER_READ) && permissions.OTHER_EXEC);
|
||||
|
||||
/**
|
||||
* Determine if the current user has read permission.
|
||||
* @return {boolean} The current user can read.
|
||||
*/
|
||||
Item.prototype.canRead = function() {
|
||||
const uid = getUid();
|
||||
const gid = getGid();
|
||||
let can = false;
|
||||
if (uid === 0) {
|
||||
can = true;
|
||||
} else if (uid === this._uid || uid !== uid) {
|
||||
// (uid !== uid) means uid is NaN, only for windows
|
||||
can = (permissions.USER_READ & this._mode) === permissions.USER_READ;
|
||||
} else if (gid === this._gid) {
|
||||
can = (permissions.GROUP_READ & this._mode) === permissions.GROUP_READ;
|
||||
} else {
|
||||
can = (permissions.OTHER_READ & this._mode) === permissions.OTHER_READ;
|
||||
}
|
||||
return can;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the current user has write permission.
|
||||
* @return {boolean} The current user can write.
|
||||
*/
|
||||
Item.prototype.canWrite = function() {
|
||||
const uid = getUid();
|
||||
const gid = getGid();
|
||||
let can = false;
|
||||
if (uid === 0) {
|
||||
can = true;
|
||||
} else if (uid === this._uid || uid !== uid) {
|
||||
// (uid !== uid) means uid is NaN, only for windows
|
||||
can = (permissions.USER_WRITE & this._mode) === permissions.USER_WRITE;
|
||||
} else if (gid === this._gid) {
|
||||
can = (permissions.GROUP_WRITE & this._mode) === permissions.GROUP_WRITE;
|
||||
} else {
|
||||
can = (permissions.OTHER_WRITE & this._mode) === permissions.OTHER_WRITE;
|
||||
}
|
||||
return can;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the current user has execute permission.
|
||||
* @return {boolean} The current user can execute.
|
||||
*/
|
||||
Item.prototype.canExecute = function() {
|
||||
const uid = getUid();
|
||||
const gid = getGid();
|
||||
let can = false;
|
||||
if (uid === 0) {
|
||||
can = true;
|
||||
} else if (uid === this._uid || isNaN(uid)) {
|
||||
// NaN occurs on windows
|
||||
can = (permissions.USER_EXEC & this._mode) === permissions.USER_EXEC;
|
||||
} else if (gid === this._gid) {
|
||||
can = (permissions.GROUP_EXEC & this._mode) === permissions.GROUP_EXEC;
|
||||
} else {
|
||||
can = (permissions.OTHER_EXEC & this._mode) === permissions.OTHER_EXEC;
|
||||
}
|
||||
return can;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get access time.
|
||||
* @return {Date} Access time.
|
||||
*/
|
||||
Item.prototype.getATime = function() {
|
||||
return this._atime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set access time.
|
||||
* @param {Date} atime Access time.
|
||||
*/
|
||||
Item.prototype.setATime = function(atime) {
|
||||
this._atime = atime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get change time.
|
||||
* @return {Date} Change time.
|
||||
*/
|
||||
Item.prototype.getCTime = function() {
|
||||
return this._ctime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set change time.
|
||||
* @param {Date} ctime Change time.
|
||||
*/
|
||||
Item.prototype.setCTime = function(ctime) {
|
||||
this._ctime = ctime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get birth time.
|
||||
* @return {Date} Birth time.
|
||||
*/
|
||||
Item.prototype.getBirthtime = function() {
|
||||
return this._birthtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set change time.
|
||||
* @param {Date} birthtime Birth time.
|
||||
*/
|
||||
Item.prototype.setBirthtime = function(birthtime) {
|
||||
this._birthtime = birthtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get modification time.
|
||||
* @return {Date} Modification time.
|
||||
*/
|
||||
Item.prototype.getMTime = function() {
|
||||
return this._mtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set modification time.
|
||||
* @param {Date} mtime Modification time.
|
||||
*/
|
||||
Item.prototype.setMTime = function(mtime) {
|
||||
this._mtime = mtime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get mode (permission only, e.g 0666).
|
||||
* @return {number} Mode.
|
||||
*/
|
||||
Item.prototype.getMode = function() {
|
||||
return this._mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set mode (permission only, e.g 0666).
|
||||
* @param {Date} mode Mode.
|
||||
*/
|
||||
Item.prototype.setMode = function(mode) {
|
||||
this.setCTime(new Date());
|
||||
this._mode = mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user id.
|
||||
* @return {number} User id.
|
||||
*/
|
||||
Item.prototype.getUid = function() {
|
||||
return this._uid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set user id.
|
||||
* @param {number} uid User id.
|
||||
*/
|
||||
Item.prototype.setUid = function(uid) {
|
||||
this.setCTime(new Date());
|
||||
this._uid = uid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get group id.
|
||||
* @return {number} Group id.
|
||||
*/
|
||||
Item.prototype.getGid = function() {
|
||||
return this._gid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set group id.
|
||||
* @param {number} gid Group id.
|
||||
*/
|
||||
Item.prototype.setGid = function(gid) {
|
||||
this.setCTime(new Date());
|
||||
this._gid = gid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item stats.
|
||||
* @return {Object} Stats properties.
|
||||
*/
|
||||
Item.prototype.getStats = function() {
|
||||
return {
|
||||
dev: 8675309,
|
||||
nlink: this.links,
|
||||
uid: this.getUid(),
|
||||
gid: this.getGid(),
|
||||
rdev: 0,
|
||||
blksize: 4096,
|
||||
ino: this._id,
|
||||
atime: this.getATime(),
|
||||
mtime: this.getMTime(),
|
||||
ctime: this.getCTime(),
|
||||
birthtime: this.getBirthtime(),
|
||||
atimeMs: +this.getATime(),
|
||||
mtimeMs: +this.getMTime(),
|
||||
ctimeMs: +this.getCTime(),
|
||||
birthtimeMs: +this.getBirthtime()
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the item's string representation.
|
||||
* @return {string} String representation.
|
||||
*/
|
||||
Item.prototype.toString = function() {
|
||||
return '[' + this.constructor.name + ']';
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
* @type {function()}
|
||||
*/
|
||||
exports = module.exports = Item;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
const {fixWin32Permissions} = require('./item');
|
||||
const path = require('path');
|
||||
const FileSystem = require('./filesystem');
|
||||
const fs = require('fs');
|
||||
const bypass = require('./bypass');
|
||||
|
||||
const createContext = ({output, options = {}, target}, newContext) =>
|
||||
Object.assign(
|
||||
{
|
||||
// Assign options and set defaults if needed
|
||||
options: {
|
||||
recursive: options.recursive !== false,
|
||||
lazy: options.lazy !== false
|
||||
},
|
||||
output,
|
||||
target
|
||||
},
|
||||
newContext
|
||||
);
|
||||
|
||||
function addFile(context, stats, isRoot) {
|
||||
const {output, target} = context;
|
||||
const {lazy} = context.options;
|
||||
|
||||
if (!stats.isFile()) {
|
||||
throw new Error(`${target} is not a valid file!`);
|
||||
}
|
||||
|
||||
const outputPropKey = isRoot ? target : path.basename(target);
|
||||
|
||||
output[outputPropKey] = () => {
|
||||
const content = !lazy ? fs.readFileSync(target) : '';
|
||||
const file = FileSystem.file(Object.assign({}, stats, {content}))();
|
||||
|
||||
if (lazy) {
|
||||
Object.defineProperty(file, '_content', {
|
||||
get() {
|
||||
const res = bypass(() => fs.readFileSync(target));
|
||||
Object.defineProperty(file, '_content', {
|
||||
value: res,
|
||||
writable: true
|
||||
});
|
||||
return res;
|
||||
},
|
||||
set(data) {
|
||||
Object.defineProperty(file, '_content', {
|
||||
value: data,
|
||||
writable: true
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
return file;
|
||||
};
|
||||
|
||||
return output[outputPropKey];
|
||||
}
|
||||
|
||||
function addDir(context, stats, isRoot) {
|
||||
const {target, output} = context;
|
||||
const {recursive} = context.options;
|
||||
|
||||
if (!stats.isDirectory()) {
|
||||
throw new Error(`${target} is not a valid directory!`);
|
||||
}
|
||||
|
||||
stats = Object.assign({}, stats);
|
||||
const outputPropKey = isRoot ? target : path.basename(target);
|
||||
|
||||
// On windows platforms, directories do not have the executable flag, which causes FileSystem.prototype.getItem
|
||||
// to think that the directory cannot be traversed. This is a workaround, however, a better solution may be to
|
||||
// re-think the logic in FileSystem.prototype.getItem
|
||||
// This workaround adds executable privileges if read privileges are found
|
||||
stats.mode = fixWin32Permissions(stats.mode);
|
||||
|
||||
// Create directory factory
|
||||
const directoryItems = {};
|
||||
output[outputPropKey] = FileSystem.directory(
|
||||
Object.assign(stats, {items: directoryItems})
|
||||
);
|
||||
|
||||
fs.readdirSync(target).forEach(p => {
|
||||
const absPath = path.join(target, p);
|
||||
const stats = fs.statSync(absPath);
|
||||
const newContext = createContext(context, {
|
||||
target: absPath,
|
||||
output: directoryItems
|
||||
});
|
||||
|
||||
if (recursive && stats.isDirectory()) {
|
||||
addDir(newContext, stats);
|
||||
} else if (stats.isFile()) {
|
||||
addFile(newContext, stats);
|
||||
}
|
||||
});
|
||||
|
||||
return output[outputPropKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load directory or file from real FS
|
||||
*/
|
||||
exports.load = function(p, options) {
|
||||
return bypass(() => {
|
||||
p = path.resolve(p);
|
||||
|
||||
const stats = fs.statSync(p);
|
||||
const context = createContext({output: {}, options, target: p});
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return addDir(context, stats, true);
|
||||
} else if (stats.isFile()) {
|
||||
return addFile(context, stats, true);
|
||||
}
|
||||
});
|
||||
};
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
|
||||
const Item = require('./item');
|
||||
|
||||
const constants = require('constants');
|
||||
|
||||
/**
|
||||
* A directory.
|
||||
* @constructor
|
||||
*/
|
||||
function SymbolicLink() {
|
||||
Item.call(this);
|
||||
|
||||
/**
|
||||
* Relative path to source.
|
||||
* @type {string}
|
||||
*/
|
||||
this._path = undefined;
|
||||
}
|
||||
util.inherits(SymbolicLink, Item);
|
||||
|
||||
/**
|
||||
* Set the path to the source.
|
||||
* @param {string} pathname Path to source.
|
||||
*/
|
||||
SymbolicLink.prototype.setPath = function(pathname) {
|
||||
this._path = pathname;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the path to the source.
|
||||
* @return {string} Path to source.
|
||||
*/
|
||||
SymbolicLink.prototype.getPath = function() {
|
||||
return this._path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get symbolic link stats.
|
||||
* @return {Object} Stats properties.
|
||||
*/
|
||||
SymbolicLink.prototype.getStats = function() {
|
||||
const size = this._path.length;
|
||||
const stats = Item.prototype.getStats.call(this);
|
||||
stats.mode = this.getMode() | constants.S_IFLNK;
|
||||
stats.size = size;
|
||||
stats.blocks = Math.ceil(size / 512);
|
||||
return stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the constructor.
|
||||
* @type {function()}
|
||||
*/
|
||||
exports = module.exports = SymbolicLink;
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "mock-fs",
|
||||
"description": "A configurable mock file system. You know, for testing.",
|
||||
"version": "4.13.0",
|
||||
"main": "lib/index.js",
|
||||
"homepage": "https://github.com/tschaub/mock-fs",
|
||||
"author": {
|
||||
"name": "Tim Schaub",
|
||||
"url": "http://tschaub.net/"
|
||||
},
|
||||
"keywords": [
|
||||
"mock",
|
||||
"fs",
|
||||
"test",
|
||||
"fixtures",
|
||||
"file system",
|
||||
"memory"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/tschaub/mock-fs.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/tschaub/mock-fs/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint benchmarks lib test",
|
||||
"pretest": "npm run lint",
|
||||
"test": "mocha --recursive test"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "tschaub"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-tschaub": "^13.1.0",
|
||||
"mocha": "^6.1.4",
|
||||
"rimraf": "^2.6.3",
|
||||
"semver": "^6.0.0"
|
||||
}
|
||||
|
||||
,"_resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz"
|
||||
,"_integrity": "sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA=="
|
||||
,"_from": "mock-fs@^4.1.0"
|
||||
}
|
||||
Reference in new issue
Block a user