Skip to content

Commit

Permalink
Merge pull request #40 from mc-zone/master
Browse files Browse the repository at this point in the history
Prettify Error message and stack display, fix #32 .
  • Loading branch information
sokra committed Jun 18, 2017
2 parents 4b96090 + 5fcc26c commit 67cec91
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 44 deletions.
6 changes: 1 addition & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"es6": true
},
"parserOptions": {
"sourceType": "module"
"sourceType": "script"
},
"rules": {
"accessor-pairs": "error",
Expand Down Expand Up @@ -74,10 +74,6 @@
"id-blacklist": "error",
"id-match": "error",
"jsx-quotes": "error",
"linebreak-style": [
"error",
"unix"
],
"lines-around-comment": "error",
"lines-around-directive": "error",
"max-depth": "error",
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language: node_js
node_js:
- "4"
- "6"
- "7"
- "8"
script: npm run travis

before_install:
Expand Down
51 changes: 21 additions & 30 deletions lib/MemoryFileSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,18 @@
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;

class MemoryFileSystemError extends Error {
constructor(err, path) {
super(err, path);
if (Error.captureStackTrace)
Error.captureStackTrace(this, this.constructor)
this.code = err.code;
this.errno = err.errno;
this.message = err.description;
this.path = path;
}
}

function isDir(item) {
if(typeof item !== "object") return false;
return item[""] === true;
Expand Down Expand Up @@ -102,7 +92,7 @@ class MemoryFileSystem {
isSocket: falseFn
};
} else {
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, "stat");
}
}

Expand All @@ -112,14 +102,14 @@ class MemoryFileSystem {
let i = 0
for(; i < path.length - 1; i++) {
if(!isDir(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
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);
throw new MemoryFileSystemError(errors.code.EISDIR, _path, "readFile");
else
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readFile");
}
current = current[path[i]];
const encoding = typeof optionsOrEncoding === "object" ? optionsOrEncoding.encoding : optionsOrEncoding;
Expand All @@ -133,14 +123,14 @@ class MemoryFileSystem {
let i = 0;
for(; i < path.length - 1; i++) {
if(!isDir(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
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);
throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "readdir");
else
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, "readdir");
}
return Object.keys(current[path[i]]).filter(Boolean);
}
Expand All @@ -151,7 +141,7 @@ class MemoryFileSystem {
let current = this.data;
for(let i = 0; i < path.length; i++) {
if(isFile(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOTDIR, _path);
throw new MemoryFileSystemError(errors.code.ENOTDIR, _path, "mkdirp");
else if(!isDir(current[path[i]]))
current[path[i]] = {"":true};
current = current[path[i]];
Expand All @@ -166,31 +156,32 @@ class MemoryFileSystem {
let i = 0;
for(; i < path.length - 1; i++) {
if(!isDir(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, "mkdir");
current = current[path[i]];
}
if(isDir(current[path[i]]))
throw new MemoryFileSystemError(errors.code.EEXIST, _path);
throw new MemoryFileSystemError(errors.code.EEXIST, _path, "mkdir");
else if(isFile(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOTDIR, _path);
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);
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);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation);
current = current[path[i]];
}
if(!testFn(current[path[i]]))
throw new MemoryFileSystemError(errors.code.ENOENT, _path);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, operation);
delete current[path[i]];
return;
}
Expand All @@ -204,24 +195,24 @@ class MemoryFileSystem {
}

readlinkSync(_path) {
throw new MemoryFileSystemError(errors.code.ENOSYS, _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);
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);
throw new MemoryFileSystemError(errors.code.ENOENT, _path, "writeFile");
current = current[path[i]];
}
if(isDir(current[path[i]]))
throw new MemoryFileSystemError(errors.code.EISDIR, _path);
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;
Expand Down
31 changes: 31 additions & 0 deletions lib/MemoryFileSystemError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

"use strict";

class MemoryFileSystemError extends Error {
constructor(err, path, operation) {
super(err, path);

// Set `name` and `message` before call `Error.captureStackTrace` \
// so that we will obtain the correct 1st line of stack, like:
// [Error]: [Message]
this.name = this.constructor.name;
var message = [`${err.code}:`, `${err.description},`];
// Add operation name and path into message, similar to node `fs` style.
if(operation) {
message.push(operation);
}
message.push(`\'${path}\'`);
this.message = message.join(' ');

this.code = err.code;
this.errno = err.errno;
this.path = path;
this.operation = operation;

if(Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}

module.exports = MemoryFileSystemError;
2 changes: 2 additions & 0 deletions lib/join.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const normalize = require("./normalize");

const absoluteWinRegExp = /^[A-Z]:([\\\/]|$)/i;
Expand Down
16 changes: 9 additions & 7 deletions lib/normalize.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"use strict";

module.exports = function normalize(path) {
var parts = path.split(/(\\+|\/+)/);
if(parts.length === 1)
return path;
var result = [];
var absolutePathStart = 0;
for(var i = 0, sep = false; i < parts.length; i++, sep = !sep) {
for(var i = 0, sep = false; i < parts.length; i += 1, sep = !sep) {
var part = parts[i];
if(i === 0 && /^([A-Z]:)?$/i.test(part)) {
result.push(part);
Expand All @@ -30,7 +32,7 @@ module.exports = function normalize(path) {
// i. e. "a/../b/c" => "b/c"
// i. e. "/../b/c" => "/b/c"
// i. e. "C:\..\a\b\c" => "C:\a\b\c"
i++;
i += 1;
sep = !sep;
result.length = absolutePathStart;
break;
Expand All @@ -42,7 +44,7 @@ module.exports = function normalize(path) {
if(absolutePathStart === 0) {
result.length -= 3;
} else {
i++;
i += 1;
sep = !sep;
result.length = 2;
}
Expand All @@ -66,9 +68,9 @@ module.exports = function normalize(path) {
// i. e. "C:\." => "C:\"
// i. e. "C:\.\a\b\c" => "C:\a\b\c"
if(absolutePathStart === 0) {
result.length--;
result.length -= 1;
} else {
i++;
i += 1;
sep = !sep;
}
break;
Expand All @@ -78,7 +80,7 @@ module.exports = function normalize(path) {
// i. e. "C:\a\." => "C:\"
// i. e. "a/./b/c" => "a/b/c"
// i. e. "/a/./b/c" => "/a/b/c"
result.length--;
result.length -= 1;
break;
}
} else if(part) {
Expand All @@ -88,4 +90,4 @@ module.exports = function normalize(path) {
if(result.length === 1 && /^[A-Za-z]:$/.test(result))
return result[0] + "\\";
return result.join("");
};
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
],
"scripts": {
"test": "mocha",
"lint": "eslint lib/*",
"cover": "istanbul cover node_modules/mocha/bin/_mocha",
"travis": "npm run cover -- --report lcovonly"
"travis": "npm run cover -- --report lcovonly && npm run lint"
},
"engines": {
"node": ">=4.3.0 <5.0.0 || >=5.10"
Expand All @@ -35,6 +36,7 @@
"bl": "^1.0.0",
"codecov.io": "^0.1.4",
"coveralls": "^2.11.2",
"eslint": "^4.0.0",
"istanbul": "0.4.5",
"mocha": "3.2.0",
"should": "^4.0.4"
Expand Down
54 changes: 54 additions & 0 deletions test/MemoryFileSystemError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
var bl = require("bl");
var should = require("should");
var MemoryFileSystem = require("../lib/MemoryFileSystem");
var MemoryFileSystemError = require("../lib/MemoryFileSystemError");

describe("error", function() {
function catchError(fn) {
try {
fn();
} catch(e) {
return e;
}
return null;
}

it("should include the path in Error message", function(done) {
var fs = new MemoryFileSystem();
var invalidPath = "/nonexist/file";
var error = catchError(function() {
fs.statSync(invalidPath);
});
error.message.should.containEql(invalidPath);

fs.readFile(invalidPath, function(err) {
err.message.should.containEql(invalidPath);
done();
});
});
it("should use correct error message in the first line of Error stack", function(done) {
var fs = new MemoryFileSystem();
fs.unlink("/test/abcd", function(error) {
error.should.be.instanceof(Error);
error.stack.should.startWith(error.name);

var firstLine = error.stack.split(/\r\n|\n/)[0];
firstLine.should.containEql(error.code);
firstLine.should.containEql(error.message);
firstLine.should.containEql(error.operation);
done();
});
});
it("should work fine without operation name", function() {
var errorData = {
code:"ETEST",
description:"testerror"
};
var errorPath = "file";
var error = new MemoryFileSystemError(errorData, errorPath);
error.message.should.startWith(error.code);
error.stack.should.startWith(error.name);
error.stack.should.containEql(error.message);
error.stack.should.containEql(errorPath);
});
});

0 comments on commit 67cec91

Please sign in to comment.