react-native/local-cli/__mocks__/fs.js

437 lines
10 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const asyncify = require('async/asyncify');
const {EventEmitter} = require('events');
const {dirname} = require.requireActual('path');
const fs = jest.genMockFromModule('fs');
const invariant = require('fbjs/lib/invariant');
const path = require('path');
const stream = require.requireActual('stream');
const noop = () => {};
function asyncCallback(cb) {
return function() {
setImmediate(() => cb.apply(this, arguments));
};
}
const mtime = {
getTime: () => Math.ceil(Math.random() * 10000000),
};
fs.realpath.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let node;
try {
node = getToNode(filepath);
} catch (e) {
return callback(e);
}
if (node && typeof node === 'object' && node.SYMLINK != null) {
return callback(null, node.SYMLINK);
}
return callback(null, filepath);
});
fs.readdirSync.mockImplementation(filepath => Object.keys(getToNode(filepath)));
fs.readdir.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let node;
try {
node = getToNode(filepath);
if (node && typeof node === 'object' && node.SYMLINK != null) {
node = getToNode(node.SYMLINK);
}
} catch (e) {
return callback(e);
}
if (!(node && typeof node === 'object' && node.SYMLINK == null)) {
return callback(new Error(filepath + ' is not a directory.'));
}
return callback(null, Object.keys(node));
});
fs.readFile.mockImplementation(asyncify(fs.readFileSync));
fs.readFileSync.mockImplementation(function(filepath, encoding) {
filepath = path.normalize(filepath);
const node = getToNode(filepath);
if (isDirNode(node)) {
throw new Error('Error readFileSync a dir: ' + filepath);
}
if (Buffer.isBuffer(node) && typeof encoding !== 'undefined') {
return node.toString();
}
return node;
});
fs.writeFile.mockImplementation(asyncify(fs.writeFileSync));
fs.writeFileSync.mockImplementation((filePath, content, options) => {
filePath = path.normalize(filePath);
if (options == null || typeof options === 'string') {
options = {encoding: options};
}
invariant(
options.encoding == null || options.encoding === 'utf8',
'`options` argument supports only `null` or `"utf8"`',
);
const dirPath = path.dirname(filePath);
const node = getToNode(dirPath);
if (!isDirNode(node)) {
throw fsError('ENOTDIR', 'not a directory: ' + dirPath);
}
node[path.basename(filePath)] = content;
});
const openFds = new Map();
let nextFd = 3;
fs.openSync.mockImplementation((filePath, flags) => {
const dirPath = path.dirname(filePath);
const node = getToNode(dirPath);
if (!isDirNode(node)) {
throw fsError('ENOTDIR', 'not a directory: ' + dirPath);
}
node[path.basename(filePath)] = '';
openFds.set(nextFd, {filePath, flags, node});
return nextFd++;
});
fs.writeSync.mockImplementation((fd, str) => {
invariant(typeof str === 'string', 'only strings supported');
const data = openFds.get(fd);
if (data == null || data.flags !== 'w') {
throw fsError('EBADF', 'bad file descriptor, write');
}
data.node[path.basename(data.filePath)] += str;
});
fs.closeSync.mockImplementation(fd => {
openFds.delete(fd);
});
fs.mkdir.mockImplementation(asyncify(fs.mkdirSync));
fs.mkdirSync.mockImplementation((dirPath, mode) => {
const parentPath = path.dirname(dirPath);
const node = getToNode(parentPath);
if (!isDirNode(node)) {
throw fsError('ENOTDIR', 'not a directory: ' + parentPath);
}
if (node[path.basename(dirPath)] == null) {
node[path.basename(dirPath)] = {};
}
});
function fsError(code, message) {
const error = new Error(code + ': ' + message);
error.code = code;
return error;
}
function isDirNode(node) {
return (
node &&
typeof node === 'object' &&
node.SYMLINK == null &&
Buffer.isBuffer(node) === false
);
}
function readlinkSync(filepath) {
const node = getToNode(filepath);
if (node !== null && typeof node === 'object' && !!node.SYMLINK) {
return node.SYMLINK;
} else {
throw new Error(`EINVAL: invalid argument, readlink '${filepath}'`);
}
}
fs.readlink.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let result;
try {
result = readlinkSync(filepath);
} catch (e) {
callback(e);
return;
}
callback(null, result);
});
fs.readlinkSync.mockImplementation(readlinkSync);
function existsSync(filepath) {
try {
const node = getToNode(filepath);
return node !== null;
} catch (e) {
return false;
}
}
fs.exists.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let result;
try {
result = existsSync(filepath);
} catch (e) {
callback(e);
return;
}
callback(null, result);
});
fs.existsSync.mockImplementation(existsSync);
function makeStatResult(node) {
const isSymlink = node != null && node.SYMLINK != null;
return {
isBlockDevice: () => false,
isCharacterDevice: () => false,
isDirectory: () => node != null && typeof node === 'object' && !isSymlink,
isFIFO: () => false,
isFile: () => node != null && typeof node === 'string',
isSocket: () => false,
isSymbolicLink: () => isSymlink,
mtime,
};
}
function statSync(filepath) {
const node = getToNode(filepath);
if (node != null && node.SYMLINK) {
return statSync(node.SYMLINK);
}
return makeStatResult(node);
}
fs.stat.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let result;
try {
result = statSync(filepath);
} catch (e) {
callback(e);
return;
}
callback(null, result);
});
fs.statSync.mockImplementation(statSync);
function lstatSync(filepath) {
const node = getToNode(filepath);
return makeStatResult(node);
}
fs.lstat.mockImplementation((filepath, callback) => {
callback = asyncCallback(callback);
let result;
try {
result = lstatSync(filepath);
} catch (e) {
callback(e);
return;
}
callback(null, result);
});
fs.lstatSync.mockImplementation(lstatSync);
fs.open.mockImplementation(function(filepath) {
const callback = arguments[arguments.length - 1] || noop;
let data, error, fd;
try {
data = getToNode(filepath);
} catch (e) {
error = e;
}
if (error || data == null) {
error = Error(`ENOENT: no such file or directory: \`${filepath}\``);
}
if (data != null) {
/* global Buffer: true */
fd = {buffer: new Buffer(data, 'utf8'), position: 0};
}
callback(error, fd);
});
fs.read.mockImplementation(
(fd, buffer, writeOffset, length, position, callback = noop) => {
let bytesWritten;
try {
if (position == null || position < 0) {
({position} = fd);
}
bytesWritten = fd.buffer.copy(
buffer,
writeOffset,
position,
position + length,
);
fd.position = position + bytesWritten;
} catch (e) {
callback(Error('invalid argument'));
return;
}
callback(null, bytesWritten, buffer);
},
);
fs.close.mockImplementation((fd, callback = noop) => {
try {
fd.buffer = fs.position = undefined;
} catch (e) {
callback(Error('invalid argument'));
return;
}
callback(null);
});
let filesystem = {};
fs.mock = {
clear() {
filesystem = {};
},
};
fs.createReadStream.mockImplementation(filepath => {
if (!filepath.startsWith('/')) {
throw Error('Cannot open file ' + filepath);
}
const parts = filepath.split('/').slice(1);
let file = filesystem;
for (const part of parts) {
file = file[part];
if (!file) {
break;
}
}
if (typeof file !== 'string') {
throw Error('Cannot open file ' + filepath);
}
return new stream.Readable({
read() {
this.push(file, 'utf8');
this.push(null);
},
});
});
fs.createWriteStream.mockImplementation(filePath => {
let node;
const writeStream = new stream.Writable({
write(chunk, encoding, callback) {
this.__chunks.push(chunk);
node[path.basename(filePath)] = this.__chunks.join('');
callback();
},
});
writeStream.__file = filePath;
writeStream.__chunks = [];
writeStream.end = jest.fn(writeStream.end);
fs.createWriteStream.mock.returned.push(writeStream);
try {
const dirPath = dirname(filePath);
node = getToNode(dirPath);
if (!isDirNode(node)) {
throw fsError('ENOTDIR', 'not a directory: ' + dirPath);
}
// Truncate the file on opening.
node[path.basename(filePath)] = '';
} catch (error) {
process.nextTick(() => writeStream.emit('error', error));
}
return writeStream;
});
fs.createWriteStream.mock.returned = [];
fs.__setMockFilesystem = object => (filesystem = object);
const watcherListByPath = new Map();
fs.watch.mockImplementation((filename, options, listener) => {
if (options.recursive) {
throw new Error('recursive watch not implemented');
}
let watcherList = watcherListByPath.get(filename);
if (watcherList == null) {
watcherList = [];
watcherListByPath.set(filename, watcherList);
}
const fsWatcher = new EventEmitter();
fsWatcher.on('change', listener);
fsWatcher.close = () => {
watcherList.splice(watcherList.indexOf(fsWatcher), 1);
fsWatcher.close = () => {
throw new Error('FSWatcher is already closed');
};
};
watcherList.push(fsWatcher);
});
fs.__triggerWatchEvent = (eventType, filename) => {
const directWatchers = watcherListByPath.get(filename) || [];
directWatchers.forEach(wtc => wtc.emit('change', eventType));
const dirPath = path.dirname(filename);
const dirWatchers = watcherListByPath.get(dirPath) || [];
dirWatchers.forEach(wtc =>
wtc.emit('change', eventType, path.relative(dirPath, filename)),
);
};
function getToNode(filepath) {
// Ignore the drive for Windows paths.
if (filepath.match(/^[a-zA-Z]:\\/)) {
filepath = filepath.substring(2);
}
if (filepath.endsWith(path.sep)) {
filepath = filepath.slice(0, -1);
}
const parts = filepath.split(/[\/\\]/);
if (parts[0] !== '') {
throw new Error('Make sure all paths are absolute.');
}
let node = filesystem;
parts.slice(1).forEach(part => {
if (node && node.SYMLINK) {
node = getToNode(node.SYMLINK);
}
node = node[part];
if (node == null) {
const err = new Error(
`ENOENT: no such file or directory: \`${filepath}\``,
);
err.code = 'ENOENT';
throw err;
}
});
return node;
}
module.exports = fs;