diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index b59f7e3..0000000
--- a/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-test/
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a7ce31d..0000000
--- a/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-SHELL := /bin/bash
-
-test:
-	@./test/run.js
-
-release:
-	git push
-	git push --tags
-	npm publish .
-
-.PHONY: test
diff --git a/Readme.md b/Readme.md
index fcd1b97..b707860 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,5 +1,7 @@
 # stack-trace
 
+[![Build Status](https://travis-ci.org/felixge/node-stack-trace.svg?branch=master)](https://travis-ci.org/felixge/node-stack-trace)
+
 Get v8 stack traces as an array of CallSite objects.
 
 ## Install
@@ -12,22 +14,22 @@ npm install stack-trace
 
 The stack-trace module makes it easy for you to capture the current stack:
 
-``` javascript
-var stackTrace = require('stack-trace');
-var trace = stackTrace.get();
+```javascript
+import { get } from 'stack-trace';
+const trace = get();
 
-require('assert').strictEqual(trace[0].getFileName(), __filename);
+expect(trace[0].getFileName()).toBe(__filename);
 ```
 
 However, sometimes you have already popped the stack you are interested in,
 and all you have left is an `Error` object. This module can help:
 
-``` javascript
-var stackTrace = require('stack-trace');
-var err = new Error('something went wrong');
-var trace = stackTrace.parse(err);
+```javascript
+import { parse } from 'stack-trace';
+const err = new Error('something went wrong');
+const trace = parse(err);
 
-require('assert').strictEqual(trace[0].getFileName(), __filename);
+expect(trace[0].getFileName()).toBe(__filename);
 ```
 
 Please note that parsing the `Error#stack` property is not perfect, only
@@ -72,7 +74,7 @@ is sometimes a little different, but still useful.
 
 ### CallSite
 
-The official v8 CallSite object API can be found [here][v8stackapi]. A quick
+The official v8 CallSite object API can be found [here][https://github.com/v8/v8/wiki/Stack-Trace-API#customizing-stack-traces]. A quick
 excerpt:
 
 > A CallSite object defines the following methods:
@@ -91,7 +93,7 @@ excerpt:
 > * **isNative**: is this call in native V8 code?
 > * **isConstructor**: is this a constructor call?
 
-[v8stackapi]: http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
+[v8stackapi]: https://v8.dev/docs/stack-trace-api
 
 ## License
 
diff --git a/__tests__/get-test.js b/__tests__/get-test.js
new file mode 100644
index 0000000..97c9460
--- /dev/null
+++ b/__tests__/get-test.js
@@ -0,0 +1,51 @@
+import { get } from "../index.js";
+
+describe("get", () => {
+  test("basic", () => {
+    (function testBasic() {
+      var trace = get();
+
+      //expect(trace[0].getFunction()).toBe(testBasic);
+      expect(trace[0].getFunctionName()).toBe('testBasic');
+      expect(trace[0].getFileName()).toBe(__filename);
+    })();
+  });
+
+  test("wrapper", () => {
+    (function testWrapper() {
+      (function testBelowFn() {
+        var trace = get(testBelowFn);
+        //expect(trace[0].getFunction()).toBe(testWrapper);
+        expect(trace[0].getFunctionName()).toBe('testWrapper');
+      })();
+    })();
+  });
+
+  test("deep", () => {
+    (function deep1() {
+      (function deep2() {
+        (function deep3() {
+          (function deep4() {
+            (function deep5() {
+              (function deep6() {
+                (function deep7() {
+                  (function deep8() {
+                    (function deep9() {
+                      (function deep10() {
+                        (function deep10() {
+                          const trace = get();
+                          const hasFirstCallSite = trace.some(callSite => callSite.getFunctionName() === 'deep1');
+                          expect(hasFirstCallSite).toBe(true);
+                        })();
+                      })();
+                    })();
+                  })();
+                })();
+              })();
+            })();
+          })();
+        })();
+      })();
+    })();
+  });
+});
\ No newline at end of file
diff --git a/__tests__/long-stack-trace-test.js b/__tests__/long-stack-trace-test.js
new file mode 100644
index 0000000..89398d6
--- /dev/null
+++ b/__tests__/long-stack-trace-test.js
@@ -0,0 +1,22 @@
+import { parse } from "../index.js";
+const _ = require('long-stack-traces');
+
+describe("long stack trace", () => {
+  test("basic", (done) => {
+    function badFn() {
+      var err = new Error('oh no');
+      var trace = parse(err);
+
+      for (var i in trace) {
+        var filename = trace[i].getFileName();
+        if (typeof filename === 'string' && filename.match(/-----/)) {
+          done();
+          return;
+        }
+      }
+      expect.fail();
+    }
+
+    setTimeout(badFn, 10);
+  });
+});
\ No newline at end of file
diff --git a/__tests__/parse-test.js b/__tests__/parse-test.js
new file mode 100644
index 0000000..350778f
--- /dev/null
+++ b/__tests__/parse-test.js
@@ -0,0 +1,204 @@
+import { get, parse } from "../index.js";
+
+describe("parse", () => {
+  test("object in method name", () => {
+    const err = {};
+    err.stack =
+      'Error: Foo\n' +
+      '    at [object Object].global.every [as _onTimeout] (/Users/hoitz/develop/test.coffee:36:3)\n' +
+      '    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)\n';
+
+    const trace = parse(err);
+    expect(trace[0].getFileName()).toBe("/Users/hoitz/develop/test.coffee");
+    expect(trace[1].getFileName()).toBe("timers.js");
+  });
+
+  test("basic", () => {
+    (function testBasic() {
+      const err = new Error('something went wrong');
+      const trace = parse(err);
+
+      expect(trace[0].getFileName()).toBe(__filename);
+      expect(trace[0].getFunctionName()).toBe('testBasic');
+    })();
+  });
+
+  test("wrapper", () => {
+    (function testWrapper() {
+      (function testBelowFn() {
+        const err = new Error('something went wrong');
+        const trace = parse(err);
+        expect(trace[0].getFunctionName()).toBe('testBelowFn');
+        expect(trace[1].getFunctionName()).toBe('testWrapper');
+      })();
+    })();
+  });
+
+  test("no stack", () => {
+    const err = { stack: undefined };
+    const trace = parse(err);
+
+    expect(trace).toStrictEqual([]);
+  });
+
+  test("test corrupt stack", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\n' +
+  '    fuck' +
+  '    at Test.run (/Users/felix/code/node-fast-or-slow/lib/test.js:45:10)\n' +
+  'oh no' +
+  '    at TestCase.run (/Users/felix/code/node-fast-or-slow/lib/test_case.js:61:8)\n';
+
+    const trace = parse(err);
+    expect(trace.length).toBe(2);
+  });
+
+  test("trace braces in path", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\n' +
+  '    at Test.run (/Users/felix (something)/code/node-fast-or-slow/lib/test.js:45:10)\n' +
+  '    at TestCase.run (/Users/felix (something)/code/node-fast-or-slow/lib/test_case.js:61:8)\n';
+
+    const trace = parse(err);
+    expect(trace.length).toBe(2);
+    expect(trace[0].getFileName()).toBe('/Users/felix (something)/code/node-fast-or-slow/lib/test.js');
+  });
+
+  test("trace without column numbers", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\n' +
+  '    at Test.fn (/Users/felix/code/node-fast-or-slow/test/fast/example/test-example.js:6)\n' +
+  '    at Test.run (/Users/felix/code/node-fast-or-slow/lib/test.js:45)';
+
+    const trace = parse(err);
+    expect(trace[0].getFileName()).toBe("/Users/felix/code/node-fast-or-slow/test/fast/example/test-example.js");
+    expect(trace[0].getLineNumber()).toBe(6);
+    expect(trace[0].getColumnNumber()).toBeNull();
+  });
+
+  test("compare real with parsed stack trace", () => {
+    var realTrace, err;
+    function TestClass() {
+    }
+    TestClass.prototype.testFunc = function () {
+      realTrace = get();
+      err = new Error('something went wrong');
+    }
+
+    var testObj = new TestClass();
+    testObj.testFunc();
+    var parsedTrace = parse(err);
+
+    realTrace.forEach(function(real, i) {
+      var parsed = parsedTrace[i];
+
+      function compare(method, exceptions) {
+        let realValue = real[method]();
+        const parsedValue = parsed[method]();
+
+        if (exceptions && typeof exceptions[i] != 'undefined') {
+          realValue = exceptions[i];
+        }
+
+        //const realJson = JSON.stringify(realValue);
+        //const parsedJson = JSON.stringify(parsedValue);
+        //console.log(method + ': ' + realJson + ' != ' + parsedJson + ' (#' + i + ')');
+        expect(realValue).toBe(parsedValue);
+      }
+
+      compare('getFileName');
+      compare('getFunctionName', {
+        2: 'Object.asyncJestTest',
+        4: 'new Promise'
+      });
+      compare('getTypeName', {
+        7: null
+      });
+      compare('getMethodName', {
+        2: 'asyncJestTest'
+      });
+      compare('getLineNumber', {
+        0: 88,
+        1: 92
+      });
+      compare('getColumnNumber', {
+        0: 13
+      });
+      compare('isNative');
+    });
+  });
+
+  test("stack with native call", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\n' +
+  '    at Test.fn (/Users/felix/code/node-fast-or-slow/test/fast/example/test-example.js:6:10)\n' +
+  '    at Test.run (/Users/felix/code/node-fast-or-slow/lib/test.js:45:10)\n' +
+  '    at TestCase.runNext (/Users/felix/code/node-fast-or-slow/lib/test_case.js:73:8)\n' +
+  '    at TestCase.run (/Users/felix/code/node-fast-or-slow/lib/test_case.js:61:8)\n' +
+  '    at Array.0 (native)\n' +
+  '    at EventEmitter._tickCallback (node.js:126:26)';
+
+    const trace = parse(err);
+    var nativeCallSite = trace[4];
+
+    expect(nativeCallSite.getFileName()).toBeNull();
+    expect(nativeCallSite.getFunctionName()).toBe('Array.0');
+    expect(nativeCallSite.getTypeName()).toBe('Array');
+    expect(nativeCallSite.getMethodName()).toBe('0');
+    expect(nativeCallSite.getLineNumber()).toBeNull();
+    expect(nativeCallSite.getColumnNumber()).toBeNull();
+    expect(nativeCallSite.isNative()).toBe(true);
+  });
+
+  test("stack with file only", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\n' +
+  '   at /Users/felix/code/node-fast-or-slow/lib/test_case.js:80:10';
+
+    const trace = parse(err);
+    var callSite = trace[0];
+
+    expect(callSite.getFileName()).toBe('/Users/felix/code/node-fast-or-slow/lib/test_case.js');
+    expect(callSite.getFunctionName()).toBeNull();
+    expect(callSite.getTypeName()).toBeNull();
+    expect(callSite.getMethodName()).toBeNull();
+    expect(callSite.getLineNumber()).toBe(80);
+    expect(callSite.getColumnNumber()).toBe(10);
+    expect(callSite.isNative()).toBe(false);
+  });
+
+  test("stack with multiline message", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: true == false\nAnd some more shit\n' +
+  '   at /Users/felix/code/node-fast-or-slow/lib/test_case.js:80:10';
+
+    const trace = parse(err);
+    var callSite = trace[0];
+
+    expect(callSite.getFileName()).toBe('/Users/felix/code/node-fast-or-slow/lib/test_case.js');
+  });
+
+  test("stack with anonymous function call", () => {
+    const err = {};
+    err.stack =
+  'AssertionError: expected [] to be arguments\n' +
+  '    at Assertion.prop.(anonymous function) (/Users/den/Projects/should.js/lib/should.js:60:14)\n';
+
+    const trace = parse(err);
+    var callSite0 = trace[0];
+
+    expect(callSite0.getFileName()).toBe('/Users/den/Projects/should.js/lib/should.js');
+    expect(callSite0.getFunctionName()).toBe('Assertion.prop.(anonymous function)');
+    expect(callSite0.getTypeName()).toBe("Assertion.prop");
+    expect(callSite0.getMethodName()).toBe("(anonymous function)");
+    expect(callSite0.getLineNumber()).toBe(60);
+    expect(callSite0.getColumnNumber()).toBe(14);
+    expect(callSite0.isNative()).toBe(false);
+  });
+});
\ No newline at end of file
diff --git a/lib/stack-trace.js b/index.js
similarity index 69%
rename from lib/stack-trace.js
rename to index.js
index cbadd58..e794ed3 100644
--- a/lib/stack-trace.js
+++ b/index.js
@@ -1,34 +1,32 @@
-exports.get = function(belowFn) {
-  var oldLimit = Error.stackTraceLimit;
+export function get(belowFn) {
+  const oldLimit = Error.stackTraceLimit;
   Error.stackTraceLimit = Infinity;
 
-  var dummyObject = {};
+  const dummyObject = {};
 
-  var v8Handler = Error.prepareStackTrace;
+  const v8Handler = Error.prepareStackTrace;
   Error.prepareStackTrace = function(dummyObject, v8StackTrace) {
     return v8StackTrace;
   };
-  Error.captureStackTrace(dummyObject, belowFn || exports.get);
+  Error.captureStackTrace(dummyObject, belowFn || get);
 
-  var v8StackTrace = dummyObject.stack;
+  const v8StackTrace = dummyObject.stack;
   Error.prepareStackTrace = v8Handler;
   Error.stackTraceLimit = oldLimit;
 
   return v8StackTrace;
-};
+}
 
-exports.parse = function(err) {
+export function parse(err) {
   if (!err.stack) {
     return [];
   }
 
-  var self = this;
-  var lines = err.stack.split('\n').slice(1);
-
+  const lines = err.stack.split('\n').slice(1);
   return lines
     .map(function(line) {
       if (line.match(/^\s*[-]{4,}$/)) {
-        return self._createParsedCallSite({
+        return createParsedCallSite({
           fileName: line,
           lineNumber: null,
           functionName: null,
@@ -39,33 +37,32 @@ exports.parse = function(err) {
         });
       }
 
-      var lineMatch = line.match(/at (?:(.+)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/);
+      const lineMatch = line.match(/at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/);
       if (!lineMatch) {
         return;
       }
 
-      var object = null;
-      var method = null;
-      var functionName = null;
-      var typeName = null;
-      var methodName = null;
-      var isNative = (lineMatch[5] === 'native');
+      let object = null;
+      let method = null;
+      let functionName = null;
+      let typeName = null;
+      let methodName = null;
+      let isNative = (lineMatch[5] === 'native');
 
       if (lineMatch[1]) {
         functionName = lineMatch[1];
-        var methodStart = functionName.lastIndexOf('.');
+        let methodStart = functionName.lastIndexOf('.');
         if (functionName[methodStart-1] == '.')
           methodStart--;
         if (methodStart > 0) {
           object = functionName.substr(0, methodStart);
           method = functionName.substr(methodStart + 1);
-          var objectEnd = object.indexOf('.Module');
+          const objectEnd = object.indexOf('.Module');
           if (objectEnd > 0) {
             functionName = functionName.substr(objectEnd + 1);
             object = object.substr(0, objectEnd);
           }
         }
-        typeName = null;
       }
 
       if (method) {
@@ -78,7 +75,7 @@ exports.parse = function(err) {
         functionName = null;
       }
 
-      var properties = {
+      const properties = {
         fileName: lineMatch[2] || null,
         lineNumber: parseInt(lineMatch[3], 10) || null,
         functionName: functionName,
@@ -88,20 +85,20 @@ exports.parse = function(err) {
         'native': isNative,
       };
 
-      return self._createParsedCallSite(properties);
+      return createParsedCallSite(properties);
     })
     .filter(function(callSite) {
       return !!callSite;
     });
-};
+}
 
 function CallSite(properties) {
-  for (var property in properties) {
+  for (const property in properties) {
     this[property] = properties[property];
   }
 }
 
-var strProperties = [
+const strProperties = [
   'this',
   'typeName',
   'functionName',
@@ -112,18 +109,21 @@ var strProperties = [
   'function',
   'evalOrigin'
 ];
-var boolProperties = [
+
+const boolProperties = [
   'topLevel',
   'eval',
   'native',
   'constructor'
 ];
+
 strProperties.forEach(function (property) {
   CallSite.prototype[property] = null;
   CallSite.prototype['get' + property[0].toUpperCase() + property.substr(1)] = function () {
     return this[property];
   }
 });
+
 boolProperties.forEach(function (property) {
   CallSite.prototype[property] = false;
   CallSite.prototype['is' + property[0].toUpperCase() + property.substr(1)] = function () {
@@ -131,6 +131,6 @@ boolProperties.forEach(function (property) {
   }
 });
 
-exports._createParsedCallSite = function(properties) {
+function createParsedCallSite(properties) {
   return new CallSite(properties);
-};
+}
diff --git a/package.json b/package.json
index 9cd0d07..f6430f3 100644
--- a/package.json
+++ b/package.json
@@ -2,20 +2,36 @@
   "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)",
   "name": "stack-trace",
   "description": "Get v8 stack traces as an array of CallSite objects.",
-  "version": "0.0.10",
+  "version": "1.0.0-pre1",
   "homepage": "https://github.com/felixge/node-stack-trace",
   "repository": {
     "type": "git",
     "url": "git://github.com/felixge/node-stack-trace.git"
   },
-  "main": "./lib/stack-trace",
+  "type": "module",
+  "main": "index.js",
+  "exports": {
+    ".": "./index.js",
+    "./package.json": "./package.json"
+  },
+  "jest": {
+    "testEnvironment": "node",
+    "transform": {
+      "^.+\\.js$": "babel-jest"
+    }
+  },
+  "scripts": {
+    "test": "jest",
+    "release": "git push && git push --tags && npm publish"
+  },
   "engines": {
-    "node": "*"
+    "node": "16"
   },
   "license": "MIT",
-  "dependencies": {},
   "devDependencies": {
-    "far": "0.0.3",
+    "@babel/preset-env": "^7.14.1",
+    "babel-jest": "^26.6.3",
+    "jest": "^26.6.3",
     "long-stack-traces": "0.1.2"
   }
 }