You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							329 lines
						
					
					
						
							8.3 KiB
						
					
					
				
			
		
		
	
	
							329 lines
						
					
					
						
							8.3 KiB
						
					
					
				/* | 
						|
	MIT License http://www.opensource.org/licenses/mit-license.php | 
						|
	Author Tobias Koppers @sokra | 
						|
*/ | 
						|
 | 
						|
var normalize = require("./normalize"); | 
						|
var errors = require("errno"); | 
						|
var stream = require("readable-stream"); | 
						|
 | 
						|
var ReadableStream = stream.Readable; | 
						|
var WritableStream = stream.Writable; | 
						|
 | 
						|
function MemoryFileSystemError(err, path) { | 
						|
	Error.call(this) | 
						|
	if (Error.captureStackTrace) | 
						|
		Error.captureStackTrace(this, arguments.callee) | 
						|
	this.code = err.code; | 
						|
	this.errno = err.errno; | 
						|
	this.message = err.description; | 
						|
	this.path = path; | 
						|
} | 
						|
MemoryFileSystemError.prototype = new Error(); | 
						|
 | 
						|
function MemoryFileSystem(data) { | 
						|
	this.data = data || {}; | 
						|
} | 
						|
module.exports = MemoryFileSystem; | 
						|
 | 
						|
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); | 
						|
	var 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; } | 
						|
 | 
						|
MemoryFileSystem.prototype.meta = function(_path) { | 
						|
	var path = pathToArray(_path); | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			return; | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	return current[path[i]]; | 
						|
} | 
						|
 | 
						|
MemoryFileSystem.prototype.existsSync = function(_path) { | 
						|
	return !!this.meta(_path); | 
						|
} | 
						|
 | 
						|
MemoryFileSystem.prototype.statSync = function(_path) { | 
						|
	var 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); | 
						|
	} | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.readFileSync = function(_path, encoding) { | 
						|
	var path = pathToArray(_path); | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	if(!isFile(current[path[i]])) { | 
						|
		if(isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.EISDIR, _path); | 
						|
		else | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
	} | 
						|
	current = current[path[i]]; | 
						|
	return encoding ? current.toString(encoding) : current; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.readdirSync = function(_path) { | 
						|
	if(_path === "/") return Object.keys(this.data).filter(Boolean); | 
						|
	var path = pathToArray(_path); | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	if(!isDir(current[path[i]])) { | 
						|
		if(isFile(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOTDIR, _path); | 
						|
		else | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
	} | 
						|
	return Object.keys(current[path[i]]).filter(Boolean); | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.mkdirpSync = function(_path) { | 
						|
	var path = pathToArray(_path); | 
						|
	if(path.length === 0) return; | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length; i++) { | 
						|
		if(isFile(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOTDIR, _path); | 
						|
		else if(!isDir(current[path[i]])) | 
						|
			current[path[i]] = {"":true}; | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	return; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.mkdirSync = function(_path) { | 
						|
	var path = pathToArray(_path); | 
						|
	if(path.length === 0) return; | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	if(isDir(current[path[i]])) | 
						|
		throw new MemoryFileSystemError(errors.code.EEXIST, _path); | 
						|
	else if(isFile(current[path[i]])) | 
						|
		throw new MemoryFileSystemError(errors.code.ENOTDIR, _path); | 
						|
	current[path[i]] = {"":true}; | 
						|
	return; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype._remove = function(_path, name, testFn) { | 
						|
	var path = pathToArray(_path); | 
						|
	if(path.length === 0) { | 
						|
		throw new MemoryFileSystemError(errors.code.EPERM, _path); | 
						|
	} | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	if(!testFn(current[path[i]])) | 
						|
		throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
	delete current[path[i]]; | 
						|
	return; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.rmdirSync = function(_path) { | 
						|
	return this._remove(_path, "Directory", isDir); | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.unlinkSync = function(_path) { | 
						|
	return this._remove(_path, "File", isFile); | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.readlinkSync = function(_path) { | 
						|
	throw new MemoryFileSystemError(errors.code.ENOSYS, _path); | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.writeFileSync = function(_path, content, encoding) { | 
						|
	if(!content && !encoding) throw new Error("No content"); | 
						|
	var path = pathToArray(_path); | 
						|
	if(path.length === 0) { | 
						|
		throw new MemoryFileSystemError(errors.code.EISDIR, _path); | 
						|
	} | 
						|
	var current = this.data; | 
						|
	for(var i = 0; i < path.length - 1; i++) { | 
						|
		if(!isDir(current[path[i]])) | 
						|
			throw new MemoryFileSystemError(errors.code.ENOENT, _path); | 
						|
		current = current[path[i]]; | 
						|
	} | 
						|
	if(isDir(current[path[i]])) | 
						|
		throw new MemoryFileSystemError(errors.code.EISDIR, _path); | 
						|
	current[path[i]] = encoding || typeof content === "string" ? new Buffer(content, encoding) : content; | 
						|
	return; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.join = require("./join"); | 
						|
MemoryFileSystem.prototype.pathToArray = pathToArray; | 
						|
MemoryFileSystem.prototype.normalize = normalize; | 
						|
 | 
						|
// stream functions | 
						|
 | 
						|
MemoryFileSystem.prototype.createReadStream = function(path, options) { | 
						|
	var stream = new ReadableStream(); | 
						|
	var done = false; | 
						|
	var 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; | 
						|
}; | 
						|
 | 
						|
MemoryFileSystem.prototype.createWriteStream = function(path, options) { | 
						|
	var stream = new WritableStream(), self = this; | 
						|
	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; | 
						|
	} | 
						|
	var bl = [ ], len = 0; | 
						|
	stream._write = function(chunk, encoding, callback) { | 
						|
		bl.push(chunk); | 
						|
		len += chunk.length; | 
						|
		self.writeFile(path, Buffer.concat(bl, len), callback); | 
						|
	} | 
						|
	return stream; | 
						|
}; | 
						|
 | 
						|
// async functions | 
						|
 | 
						|
["stat", "readdir", "mkdirp", "rmdir", "unlink", "readlink"].forEach(function(fn) { | 
						|
	MemoryFileSystem.prototype[fn] = function(path, callback) { | 
						|
		try { | 
						|
			var 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; | 
						|
		} | 
						|
		try { | 
						|
			var result = this[fn + "Sync"](path, optArg); | 
						|
		} catch(e) { | 
						|
			setImmediate(function() { | 
						|
				callback(e); | 
						|
			}); | 
						|
 | 
						|
			return; | 
						|
		} | 
						|
		setImmediate(function() { | 
						|
			callback(null, result); | 
						|
		}); | 
						|
	}; | 
						|
}); | 
						|
 | 
						|
MemoryFileSystem.prototype.exists = function(path, callback) { | 
						|
	return callback(this.existsSync(path)); | 
						|
} | 
						|
 | 
						|
MemoryFileSystem.prototype.writeFile = function (path, content, encoding, callback) { | 
						|
	if(!callback) { | 
						|
		callback = encoding; | 
						|
		encoding = undefined; | 
						|
	} | 
						|
	try { | 
						|
		this.writeFileSync(path, content, encoding); | 
						|
	} catch(e) { | 
						|
		return callback(e); | 
						|
	} | 
						|
	return callback(); | 
						|
};
 | 
						|
 |