| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const normalize = require("./normalize");const join = require("./join");const MemoryFileSystemError = require("./MemoryFileSystemError");const errors = require("errno");const stream = require("readable-stream");const ReadableStream = stream.Readable;const WritableStream = stream.Writable;function isDir(item) {	if(typeof item !== "object") return false;	return item[""] === true;}function isFile(item) {	if(typeof item !== "object") return false;	return !item[""];}function pathToArray(path) {	path = normalize(path);	const nix = /^\//.test(path);	if(!nix) {		if(!/^[A-Za-z]:/.test(path)) {			throw new MemoryFileSystemError(errors.code.EINVAL, path);		}		path = path.replace(/[\\\/]+/g, "\\"); // multi slashs		path = path.split(/[\\\/]/);		path[0] = path[0].toUpperCase();	} else {		path = path.replace(/\/+/g, "/"); // multi slashs		path = path.substr(1).split("/");	}	if(!path[path.length-1]) path.pop();	return path;}function trueFn() { return true; }function falseFn() { return false; }class MemoryFileSystem {	constructor(data) {		this.data = data || {};		this.join = join;		this.pathToArray = pathToArray;		this.normalize = normalize;	}	meta(_path) {		const path = pathToArray(_path);		let current = this.data;		let i = 0;		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				return;			current = current[path[i]];		}		return current[path[i]];	}	existsSync(_path) {		return !!this.meta(_path);	}	statSync(_path) {		let current = this.meta(_path);		if(_path === "/" || isDir(current)) {			return {				isFile: falseFn,				isDirectory: trueFn,				isBlockDevice: falseFn,				isCharacterDevice: falseFn,				isSymbolicLink: falseFn,				isFIFO: falseFn,				isSocket: falseFn			};		} else if(isFile(current)) {			return {				isFile: trueFn,				isDirectory: falseFn,				isBlockDevice: falseFn,				isCharacterDevice: falseFn,				isSymbolicLink: falseFn,				isFIFO: falseFn,				isSocket: falseFn			};		} else {			throw new MemoryFileSystemError(errors.code.ENOENT, _path, "stat");		}	}	readFileSync(_path, optionsOrEncoding) {		const path = pathToArray(_path);		let current = this.data;		let i = 0		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readFile");			current = current[path[i]];		}		if(!isFile(current[path[i]])) {			if(isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.EISDIR, _path, "readFile");			else				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readFile");		}		current = current[path[i]];		const encoding = typeof optionsOrEncoding === "object" ? optionsOrEncoding.encoding : optionsOrEncoding;		return encoding ? current.toString(encoding) : current;	}	readdirSync(_path) {		if(_path === "/") return Object.keys(this.data).filter(Boolean);		const path = pathToArray(_path);		let current = this.data;		let i = 0;		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readdir");			current = current[path[i]];		}		if(!isDir(current[path[i]])) {			if(isFile(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "readdir");			else				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readdir");		}		return Object.keys(current[path[i]]).filter(Boolean);	}	mkdirpSync(_path) {		const path = pathToArray(_path);		if(path.length === 0) return;		let current = this.data;		for(let i = 0; i < path.length; i++) {			if(isFile(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "mkdirp");			else if(!isDir(current[path[i]]))				current[path[i]] = {"":true};			current = current[path[i]];		}		return;	}	mkdirSync(_path) {		const path = pathToArray(_path);		if(path.length === 0) return;		let current = this.data;		let i = 0;		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "mkdir");			current = current[path[i]];		}		if(isDir(current[path[i]]))			throw new MemoryFileSystemError(errors.code.EEXIST, _path, "mkdir");		else if(isFile(current[path[i]]))			throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "mkdir");		current[path[i]] = {"":true};		return;	}	_remove(_path, name, testFn) {		const path = pathToArray(_path);		const operation = name === "File" ? "unlink" : "rmdir";		if(path.length === 0) {			throw new MemoryFileSystemError(errors.code.EPERM, _path, operation);		}		let current = this.data;		let i = 0;		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation);			current = current[path[i]];		}		if(!testFn(current[path[i]]))			throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation);		delete current[path[i]];		return;	}	rmdirSync(_path) {		return this._remove(_path, "Directory", isDir);	}	unlinkSync(_path) {		return this._remove(_path, "File", isFile);	}	readlinkSync(_path) {		throw new MemoryFileSystemError(errors.code.ENOSYS, _path, "readlink");	}	writeFileSync(_path, content, optionsOrEncoding) {		if(!content && !optionsOrEncoding) throw new Error("No content");		const path = pathToArray(_path);		if(path.length === 0) {			throw new MemoryFileSystemError(errors.code.EISDIR, _path, "writeFile");		}		let current = this.data;		let i = 0		for(; i < path.length - 1; i++) {			if(!isDir(current[path[i]]))				throw new MemoryFileSystemError(errors.code.ENOENT, _path, "writeFile");			current = current[path[i]];		}		if(isDir(current[path[i]]))			throw new MemoryFileSystemError(errors.code.EISDIR, _path, "writeFile");		const encoding = typeof optionsOrEncoding === "object" ? optionsOrEncoding.encoding : optionsOrEncoding;		current[path[i]] = optionsOrEncoding || typeof content === "string" ? new Buffer(content, encoding) : content;		return;	}	// stream methods	createReadStream(path, options) {		let stream = new ReadableStream();		let done = false;		let data;		try {			data = this.readFileSync(path);		} catch (e) {			stream._read = function() {				if (done) {					return;				}				done = true;				this.emit('error', e);				this.push(null);			};			return stream;		}		options = options || { };		options.start = options.start || 0;		options.end = options.end || data.length;		stream._read = function() {			if (done) {				return;			}			done = true;			this.push(data.slice(options.start, options.end));			this.push(null);		};		return stream;	}	createWriteStream(path) {		let stream = new WritableStream();		try {			// Zero the file and make sure it is writable			this.writeFileSync(path, new Buffer(0));		} catch(e) {			// This or setImmediate?			stream.once('prefinish', function() {				stream.emit('error', e);			});			return stream;		}		let bl = [ ], len = 0;		stream._write = (chunk, encoding, callback) => {			bl.push(chunk);			len += chunk.length;			this.writeFile(path, Buffer.concat(bl, len), callback);		}		return stream;	}	// async functions	exists(path, callback) {		return callback(this.existsSync(path));	}	writeFile(path, content, encoding, callback) {		if(!callback) {			callback = encoding;			encoding = undefined;		}		try {			this.writeFileSync(path, content, encoding);		} catch(e) {			return callback(e);		}		return callback();	}}// async functions["stat", "readdir", "mkdirp", "rmdir", "unlink", "readlink"].forEach(function(fn) {	MemoryFileSystem.prototype[fn] = function(path, callback) {		let result;		try {			result = this[fn + "Sync"](path);		} catch(e) {			setImmediate(function() {				callback(e);			});			return;		}		setImmediate(function() {			callback(null, result);		});	};});["mkdir", "readFile"].forEach(function(fn) {	MemoryFileSystem.prototype[fn] = function(path, optArg, callback) {		if(!callback) {			callback = optArg;			optArg = undefined;		}		let result;		try {			result = this[fn + "Sync"](path, optArg);		} catch(e) {			setImmediate(function() {				callback(e);			});			return;		}		setImmediate(function() {			callback(null, result);		});	};});module.exports = MemoryFileSystem;
 |