From cd544cbbc59adf3fbe8e7f062ce9662a7a2dc4f7 Mon Sep 17 00:00:00 2001
From: Jason Mulligan <jason.mulligan@avoidwork.com>
Date: Tue, 29 Sep 2015 20:02:31 -0400
Subject: [PATCH] Getting it functional

---
 lib/index.js  | 68 +++++++++++++++++++++++++++++++++++++------
 lib/noop.js   |  3 ++
 lib/worker.js | 80 +++++++++++++++++++++++++++++++++++++++++++++------
 src/index.js  | 50 +++++++++++++++++++++++++++-----
 src/noop.js   |  1 +
 src/worker.js | 75 +++++++++++++++++++++++++++++++++++++++++------
 6 files changed, 246 insertions(+), 31 deletions(-)
 create mode 100644 lib/noop.js
 create mode 100644 src/noop.js

diff --git a/lib/index.js b/lib/index.js
index 511bf67..471a6d2 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,15 +1,67 @@
 "use strict";
 
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
 var path = require("path");
-var worker = require(path.join(__dirname, "worker.js"));
+var fork = require("child_process").fork;
+var worker = path.join(__dirname, "worker.js");
+var events = /^(error|message)$/;
+
+var Worker = (function () {
+	function Worker(arg) {
+		var _this = this;
+
+		_classCallCheck(this, Worker);
+
+		var isfn = typeof arg === "function",
+		    input = isfn ? arg.toString() : arg;
+
+		this.child = fork(worker);
+		this.onerror = undefined;
+		this.onmessage = undefined;
+
+		this.child.on("error", function (e) {
+			if (_this.onerror) {
+				_this.onerror.call(_this, e);
+			}
+		});
+
+		this.child.on("message", function (msg) {
+			if (_this.onmessage) {
+				_this.onmessage.call(_this, JSON.parse(msg));
+			}
+		});
 
-function factory(arg) {
-	var fn = typeof arg === "function",
-	    obj = undefined;
+		this.child.send({ input: input, isfn: isfn });
+	}
 
-	obj = worker(arg, fn);
+	_createClass(Worker, [{
+		key: "addEventListener",
+		value: function addEventListener(event, fn) {
+			if (events.test(event)) {
+				this["on" + event] = fn;
+			}
+		}
+	}, {
+		key: "close",
+		value: function close() {
+			this.child.kill();
+		}
+	}, {
+		key: "postMessage",
+		value: function postMessage(msg) {
+			this.child.send(JSON.stringify({ data: msg }));
+		}
+	}, {
+		key: "terminate",
+		value: function terminate() {
+			this.child.kill();
+		}
+	}]);
 
-	return obj;
-}
+	return Worker;
+})();
 
-module.exports = factory;
+module.exports = Worker;
diff --git a/lib/noop.js b/lib/noop.js
new file mode 100644
index 0000000..7f0f4e8
--- /dev/null
+++ b/lib/noop.js
@@ -0,0 +1,3 @@
+"use strict";
+
+module.exports = function () {};
diff --git a/lib/worker.js b/lib/worker.js
index 35e0866..6d59a12 100644
--- a/lib/worker.js
+++ b/lib/worker.js
@@ -1,15 +1,79 @@
 "use strict";
 
-var spawn = require("child_process").spawn;
+var fs = require("fs");
+var path = require("path");
+var vm = require("vm");
+var noop = path.join(__dirname, "noop.js");
 
-function factory(arg) {
-	var ps = spawn("grep", [arg]);
+function trim(arg) {
+	return arg.replace(/^(\s+|\t+|\n+)|(\s+|\t+|\n+)$/g, "");
+}
 
-	ps.on("close", function (code, signal) {
-		console.log("child process terminated due to receipt of signal " + signal);
-	});
+function explode(arg) {
+	return trim(arg).split(new RegExp("\\s*,\\s*"));
+}
 
-	return ps;
+function toFunction(arg) {
+	var args = trim(arg.replace(/^.*\(/, "").replace(/[\t|\r|\n|\"|\']+/g, "").replace(/\).*/, "")),
+	    body = trim(arg.replace(/^.*\{/, "").replace(/\}$/, ""));
+
+	return Function.apply(Function, explode(args).concat([body]));
 }
 
-module.exports = factory;
+// Bootstraps the Worker
+process.once("message", function (obj) {
+	var exp = obj.isfn ? toFunction(obj.input) : fs.readFileSync(obj.input, "utf8"),
+	    sexp = undefined;
+
+	global.self = {
+		postMessage: function postMessage(msg) {
+			process.send(JSON.stringify({ data: msg }));
+		},
+		onmessage: noop,
+		onerror: noop,
+		addEventListener: function addEventListener(event, fn) {
+			if (event === "message") {
+				global.onmessage = global.self.onmessage = fn;
+			}
+
+			if (event === "error") {
+				global.onerror = global.self.onerror = fn;
+			}
+		}
+	};
+
+	global.importScripts = function () {
+		var script = undefined,
+		    scripts = undefined;
+
+		for (var _len = arguments.length, files = Array(_len), _key = 0; _key < _len; _key++) {
+			files[_key] = arguments[_key];
+		}
+
+		scripts = files.map(function (file) {
+			return fs.readFileSync(file, "utf8");
+		}).join("\n");
+
+		script = vm.createScript(scripts);
+		script.runInThisContext();
+	};
+
+	Object.keys(global.self).forEach(function (key) {
+		global[key] = global.self[key];
+	});
+
+	process.on("message", function (msg) {
+		global.self.onmessage(JSON.parse(msg));
+	});
+
+	process.on("error", function (err) {
+		global.self.onerror(err);
+	});
+
+	if (typeof exp === "function") {
+		exp();
+	} else {
+		sexp = vm.createScript(exp);
+		sexp.runInThisContext();
+	}
+});
diff --git a/src/index.js b/src/index.js
index b884591..af7957d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,13 +1,49 @@
 const path = require("path");
-const worker = require(path.join(__dirname, "worker.js"));
+const fork = require("child_process").fork;
+const worker = path.join(__dirname, "worker.js");
+const events = /^(error|message)$/;
 
-function factory (arg) {
-	let fn = typeof arg === "function",
-		obj;
+class Worker {
+	constructor (arg) {
+		let isfn = typeof arg === "function",
+			input = isfn ? arg.toString() : arg;
 
-	obj = worker(arg, fn);
+		this.child = fork(worker);
+		this.onerror = undefined;
+		this.onmessage = undefined;
 
-	return obj;
+		this.child.on("error", e => {
+			if (this.onerror) {
+				this.onerror.call(this, e);
+			}
+		});
+
+		this.child.on("message", msg => {
+			if (this.onmessage) {
+				this.onmessage.call(this, JSON.parse(msg));
+			}
+		});
+
+		this.child.send({input: input, isfn: isfn});
+	}
+
+	addEventListener (event, fn) {
+		if (events.test(event)) {
+			this["on" + event] = fn;
+		}
+	}
+
+	close () {
+		this.child.kill();
+	}
+
+	postMessage (msg) {
+		this.child.send(JSON.stringify({data: msg}));
+	}
+
+	terminate () {
+		this.child.kill();
+	}
 }
 
-module.exports = factory;
+module.exports = Worker;
diff --git a/src/noop.js b/src/noop.js
new file mode 100644
index 0000000..ea41b01
--- /dev/null
+++ b/src/noop.js
@@ -0,0 +1 @@
+module.exports = function () {};
diff --git a/src/worker.js b/src/worker.js
index c123c88..7e1a6cc 100644
--- a/src/worker.js
+++ b/src/worker.js
@@ -1,13 +1,72 @@
-const spawn = require("child_process").spawn;
+const fs = require("fs");
+const path = require("path");
+const vm = require("vm");
+const noop = path.join(__dirname, "noop.js");
 
-function factory (arg) {
-	let ps = spawn("grep", [arg]);
+function trim (arg) {
+	return arg.replace(/^(\s+|\t+|\n+)|(\s+|\t+|\n+)$/g, "");
+}
 
-	ps.on("close", function (code, signal) {
-		console.log("child process terminated due to receipt of signal " + signal);
-	});
+function explode (arg) {
+	return trim(arg).split(new RegExp("\\s*,\\s*"));
+}
+
+function toFunction (arg) {
+	let args = trim(arg.replace(/^.*\(/, "").replace(/[\t|\r|\n|\"|\']+/g, "").replace(/\).*/, "")),
+		body = trim(arg.replace(/^.*\{/, "").replace(/\}$/, ""));
 
-	return ps;
+	return Function.apply(Function, explode(args).concat([body]));
 }
 
-module.exports = factory;
+// Bootstraps the Worker
+process.once("message", function (obj) {
+	let exp = obj.isfn ? toFunction(obj.input) : fs.readFileSync(obj.input, "utf8"),
+		sexp;
+
+	global.self = {
+		postMessage: function (msg) {
+			process.send(JSON.stringify({data: msg}));
+		},
+		onmessage: noop,
+		onerror: noop,
+		addEventListener: function (event, fn) {
+			if (event === "message") {
+				global.onmessage = global.self.onmessage = fn;
+			}
+
+			if (event === "error") {
+				global.onerror = global.self.onerror = fn;
+			}
+		}
+	};
+
+	global.importScripts = function (...files) {
+		let script, scripts;
+
+		scripts = files.map(function (file) {
+			return fs.readFileSync(file, "utf8");
+		}).join("\n");
+
+		script = vm.createScript(scripts);
+		script.runInThisContext();
+	};
+
+	Object.keys(global.self).forEach(function (key) {
+		global[key] = global.self[key];
+	});
+
+	process.on("message", function (msg) {
+		global.self.onmessage(JSON.parse(msg));
+	});
+
+	process.on("error", function (err) {
+		global.self.onerror(err);
+	});
+
+	if (typeof exp === "function") {
+		exp();
+	} else {
+		sexp = vm.createScript(exp);
+		sexp.runInThisContext();
+	}
+});
-- 
GitLab