diff --git a/lib/index.js b/lib/index.js index 511bf6789c2af48fcda886f07231e7b7455aa38e..471a6d2fd0143e2c98c516cca8182d2a7416603a 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 0000000000000000000000000000000000000000..7f0f4e86c7b0e41447cc9d3537354a125e033003 --- /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 35e0866ef95f581dc6e38100d7e3f3f3744d72f8..6d59a12901a4f3a9ff5df50a0f41fc288cd28d84 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 b884591dcc349900b4ad1c3a25a05c7a35c6db31..af7957dd5f5fd7320604914db68364e4adcfc008 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 0000000000000000000000000000000000000000..ea41b01de465176d8debbabb17bf4f343a9e0c7f --- /dev/null +++ b/src/noop.js @@ -0,0 +1 @@ +module.exports = function () {}; diff --git a/src/worker.js b/src/worker.js index c123c888b9805ee8d4a4ccd94d4f164ab54d03ba..7e1a6cc7a4e8c6bc07cb5488cadbe5a938fb3bad 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(); + } +});