Initial commit

This commit is contained in:
Mario Voigt
2020-07-30 01:16:18 +02:00
parent 9ead5330fa
commit fc012a2896
2983 changed files with 568355 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,50 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at paul@paulbetts.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/

View File

@ -0,0 +1,7 @@
Copyright (c) 2016 Paul Betts
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1 @@
npm i && npm t

View File

@ -0,0 +1,2 @@
#!/bin/bash
npm i && npm t

View File

@ -0,0 +1,26 @@
{
"source": "./src",
"destination": "./docs",
"includes": ["\\.(js|es6)$"],
"excludes": ["\\.config\\.(js|es6)$"],
"access": ["public", "protected"],
"autoPrivate": true,
"unexportIdentifier": false,
"undocumentIdentifier": true,
"builtinExternal": true,
"index": "./README.md",
"package": "./package.json",
"coverage": true,
"includeSource": true,
"title": "electron-compilers",
"plugins": [
{"name": "esdoc-es7-plugin"},
{"name": "esdoc-plugin-async-to-sync"}
],
"test": {
"type": "mocha",
"source": "./test",
"includes": ["\\.(js|es6)$"]
},
"lint": true
}

View File

@ -0,0 +1,352 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.findActualExecutable = findActualExecutable;
exports.spawnDetached = spawnDetached;
exports.spawn = spawn;
exports.spawnDetachedPromise = spawnDetachedPromise;
exports.spawnPromise = spawnPromise;
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _net = require('net');
var _net2 = _interopRequireDefault(_net);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
require('rxjs/add/observable/of');
require('rxjs/add/observable/merge');
require('rxjs/add/operator/pluck');
require('rxjs/add/operator/reduce');
var _Observable = require('rxjs/Observable');
var _Subscription = require('rxjs/Subscription');
var _AsyncSubject = require('rxjs/AsyncSubject');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const spawnOg = require('child_process').spawn;
const isWindows = process.platform === 'win32';
const d = require('debug')('spawn-rx');
/**
* stat a file but don't throw if it doesn't exist
*
* @param {string} file The path to a file
* @return {Stats} The stats structure
*
* @private
*/
function statSyncNoException(file) {
try {
return _fs2.default.statSync(file);
} catch (e) {
return null;
}
}
/**
* Search PATH to see if a file exists in any of the path folders.
*
* @param {string} exe The file to search for
* @return {string} A fully qualified path, or the original path if nothing
* is found
*
* @private
*/
function runDownPath(exe) {
// NB: Windows won't search PATH looking for executables in spawn like
// Posix does
// Files with any directory path don't get this applied
if (exe.match(/[\\\/]/)) {
d('Path has slash in directory, bailing');
return exe;
}
let target = _path2.default.join('.', exe);
if (statSyncNoException(target)) {
d(`Found executable in currect directory: ${ target }`);
return target;
}
let haystack = process.env.PATH.split(isWindows ? ';' : ':');
for (let p of haystack) {
let needle = _path2.default.join(p, exe);
if (statSyncNoException(needle)) return needle;
}
d('Failed to find executable anywhere in path');
return exe;
}
/**
* Finds the actual executable and parameters to run on Windows. This method
* mimics the POSIX behavior of being able to run scripts as executables by
* replacing the passed-in executable with the script runner, for PowerShell,
* CMD, and node scripts.
*
* This method also does the work of running down PATH, which spawn on Windows
* also doesn't do, unlike on POSIX.
*
* @param {string} exe The executable to run
* @param {Array<string>} args The arguments to run
*
* @return {Object} The cmd and args to run
* @property {string} cmd The command to pass to spawn
* @property {Array<string>} args The arguments to pass to spawn
*/
function findActualExecutable(exe, args) {
// POSIX can just execute scripts directly, no need for silly goosery
if (process.platform !== 'win32') return { cmd: runDownPath(exe), args: args };
if (!_fs2.default.existsSync(exe)) {
// NB: When you write something like `surf-client ... -- surf-build` on Windows,
// a shell would normally convert that to surf-build.cmd, but since it's passed
// in as an argument, it doesn't happen
const possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];
for (let ext of possibleExts) {
let possibleFullPath = runDownPath(`${ exe }${ ext }`);
if (_fs2.default.existsSync(possibleFullPath)) {
return findActualExecutable(possibleFullPath, args);
}
}
}
if (exe.match(/\.ps1$/i)) {
let cmd = _path2.default.join(process.env.SYSTEMROOT, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
let psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
return { cmd: cmd, args: psargs.concat(args) };
}
if (exe.match(/\.(bat|cmd)$/i)) {
let cmd = _path2.default.join(process.env.SYSTEMROOT, 'System32', 'cmd.exe');
let cmdArgs = ['/C', `${ exe } ${ args.join(' ') }`];
return { cmd: cmd, args: cmdArgs };
}
if (exe.match(/\.(js)$/i)) {
let cmd = process.execPath;
let nodeArgs = [exe];
return { cmd: cmd, args: nodeArgs.concat(args) };
}
// Dunno lol
return { cmd: exe, args: args };
}
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group that can be killed by unsubscribing from the
* return Observable.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a detached process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
function spawnDetached(exe, params) {
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var _findActualExecutable = findActualExecutable(exe, params);
let cmd = _findActualExecutable.cmd,
args = _findActualExecutable.args;
if (!isWindows) return spawn(cmd, args, Object.assign({}, opts || {}, { detached: true }));
const newParams = [cmd].concat(args);
let target = _path2.default.join(__dirname, '..', 'vendor', 'jobber', 'jobber.exe');
let options = Object.assign({}, opts || {}, { detached: true, jobber: true });
d(`spawnDetached: ${ target }, ${ newParams }`);
return spawn(target, newParams, options);
}
/**
* Spawns a process attached as a child of the current process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a child process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
function spawn(exe) {
let params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
opts = opts || {};
let spawnObs = _Observable.Observable.create(subj => {
let proc = null;
var _findActualExecutable2 = findActualExecutable(exe, params);
let cmd = _findActualExecutable2.cmd,
args = _findActualExecutable2.args;
d(`spawning process: ${ cmd } ${ args.join() }, ${ JSON.stringify(opts) }`);
let origOpts = Object.assign({}, opts);
if ('jobber' in origOpts) delete origOpts.jobber;
if ('split' in origOpts) delete origOpts.split;
proc = spawnOg(cmd, args, origOpts);
let bufHandler = source => b => {
if (b.length < 1) return;
let chunk = "<< String sent back was too long >>";
try {
chunk = b.toString();
} catch (e) {
chunk = `<< Lost chunk of process output for ${ exe } - length was ${ b.length }>>`;
}
subj.next({ source: source, text: chunk });
};
let ret = new _Subscription.Subscription();
if (opts.stdin) {
if (proc.stdin) {
ret.add(opts.stdin.subscribe(x => proc.stdin.write(x), subj.error, () => proc.stdin.end()));
} else {
subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
}
}
let stderrCompleted = null;
let stdoutCompleted = null;
let noClose = false;
if (proc.stdout) {
stdoutCompleted = new _AsyncSubject.AsyncSubject();
proc.stdout.on('data', bufHandler('stdout'));
proc.stdout.on('close', () => {
stdoutCompleted.next(true);stdoutCompleted.complete();
});
} else {
stdoutCompleted = _Observable.Observable.of(true);
}
if (proc.stderr) {
stderrCompleted = new _AsyncSubject.AsyncSubject();
proc.stderr.on('data', bufHandler('stderr'));
proc.stderr.on('close', () => {
stderrCompleted.next(true);stderrCompleted.complete();
});
} else {
stderrCompleted = _Observable.Observable.of(true);
}
proc.on('error', e => {
noClose = true;
subj.error(e);
});
proc.on('close', code => {
noClose = true;
let pipesClosed = _Observable.Observable.merge(stdoutCompleted, stderrCompleted).reduce(acc => acc, true);
if (code === 0) {
pipesClosed.subscribe(() => subj.complete());
} else {
pipesClosed.subscribe(() => subj.error(new Error(`Failed with exit code: ${ code }`)));
}
});
ret.add(new _Subscription.Subscription(() => {
if (noClose) return;
d(`Killing process: ${ cmd } ${ args.join() }`);
if (opts.jobber) {
// NB: Connecting to Jobber's named pipe will kill it
_net2.default.connect(`\\\\.\\pipe\\jobber-${ proc.pid }`);
setTimeout(() => proc.kill(), 5 * 1000);
} else {
proc.kill();
}
}));
return ret;
});
return opts.split ? spawnObs : spawnObs.pluck('text');
}
function wrapObservableInPromise(obs) {
return new Promise((res, rej) => {
let out = '';
obs.subscribe(x => out += x, e => rej(new Error(`${ out }\n${ e.message }`)), () => res(out));
});
}
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a detached
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
function spawnDetachedPromise(exe, params) {
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
return wrapObservableInPromise(spawnDetached(exe, params, opts));
}
/**
* Spawns a process as a child process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a child
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
function spawnPromise(exe, params) {
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
return wrapObservableInPromise(spawn(exe, params, opts));
}

View File

@ -0,0 +1,317 @@
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
var path = require("path");
var net = require("net");
var sfs = require("fs");
var assign = require("lodash.assign");
require("rxjs/add/observable/of");
require("rxjs/add/observable/merge");
require("rxjs/add/operator/pluck");
require("rxjs/add/operator/reduce");
var Observable_1 = require("rxjs/Observable");
var Subscription_1 = require("rxjs/Subscription");
var AsyncSubject_1 = require("rxjs/AsyncSubject");
var spawnOg = require('child_process').spawn; //tslint:disable-line:no-var-requires
var isWindows = process.platform === 'win32';
var d = require('debug')('spawn-rx'); //tslint:disable-line:no-var-requires
/**
* stat a file but don't throw if it doesn't exist
*
* @param {string} file The path to a file
* @return {Stats} The stats structure
*
* @private
*/
function statSyncNoException(file) {
try {
return sfs.statSync(file);
}
catch (e) {
return null;
}
}
/**
* Search PATH to see if a file exists in any of the path folders.
*
* @param {string} exe The file to search for
* @return {string} A fully qualified path, or the original path if nothing
* is found
*
* @private
*/
function runDownPath(exe) {
// NB: Windows won't search PATH looking for executables in spawn like
// Posix does
// Files with any directory path don't get this applied
if (exe.match(/[\\\/]/)) {
d('Path has slash in directory, bailing');
return exe;
}
var target = path.join('.', exe);
if (statSyncNoException(target)) {
d("Found executable in currect directory: " + target);
return target;
}
var haystack = process.env.PATH.split(isWindows ? ';' : ':');
for (var _i = 0, haystack_1 = haystack; _i < haystack_1.length; _i++) {
var p = haystack_1[_i];
var needle = path.join(p, exe);
if (statSyncNoException(needle)) {
return needle;
}
;
}
d('Failed to find executable anywhere in path');
return exe;
}
/**
* Finds the actual executable and parameters to run on Windows. This method
* mimics the POSIX behavior of being able to run scripts as executables by
* replacing the passed-in executable with the script runner, for PowerShell,
* CMD, and node scripts.
*
* This method also does the work of running down PATH, which spawn on Windows
* also doesn't do, unlike on POSIX.
*
* @param {string} exe The executable to run
* @param {Array<string>} args The arguments to run
*
* @return {Object} The cmd and args to run
* @property {string} cmd The command to pass to spawn
* @property {Array<string>} args The arguments to pass to spawn
*/
function findActualExecutable(exe, args) {
// POSIX can just execute scripts directly, no need for silly goosery
if (process.platform !== 'win32') {
return { cmd: runDownPath(exe), args: args };
}
if (!sfs.existsSync(exe)) {
// NB: When you write something like `surf-client ... -- surf-build` on Windows,
// a shell would normally convert that to surf-build.cmd, but since it's passed
// in as an argument, it doesn't happen
var possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];
for (var _i = 0, possibleExts_1 = possibleExts; _i < possibleExts_1.length; _i++) {
var ext = possibleExts_1[_i];
var possibleFullPath = runDownPath("" + exe + ext);
if (sfs.existsSync(possibleFullPath)) {
return findActualExecutable(possibleFullPath, args);
}
}
}
if (exe.match(/\.ps1$/i)) {
var cmd = path.join(process.env.SYSTEMROOT, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
var psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
return { cmd: cmd, args: psargs.concat(args) };
}
if (exe.match(/\.(bat|cmd)$/i)) {
var cmd = path.join(process.env.SYSTEMROOT, 'System32', 'cmd.exe');
var cmdArgs = ['/C', exe].concat(args);
return { cmd: cmd, args: cmdArgs };
}
if (exe.match(/\.(js)$/i)) {
var cmd = process.execPath;
var nodeArgs = [exe];
return { cmd: cmd, args: nodeArgs.concat(args) };
}
// Dunno lol
return { cmd: exe, args: args };
}
exports.findActualExecutable = findActualExecutable;
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group that can be killed by unsubscribing from the
* return Observable.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a detached process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
function spawnDetached(exe, params, opts) {
if (opts === void 0) { opts = null; }
var _a = findActualExecutable(exe, params), cmd = _a.cmd, args = _a.args;
if (!isWindows) {
return spawn(cmd, args, assign({}, opts || {}, { detached: true }));
}
;
var newParams = [cmd].concat(args);
var target = path.join(__dirname, '..', '..', 'vendor', 'jobber', 'Jobber.exe');
var options = assign({}, opts || {}, { detached: true, jobber: true });
d("spawnDetached: " + target + ", " + newParams);
return spawn(target, newParams, options);
}
exports.spawnDetached = spawnDetached;
/**
* Spawns a process attached as a child of the current process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a child process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
function spawn(exe, params, opts) {
if (params === void 0) { params = []; }
if (opts === void 0) { opts = null; }
opts = opts || {};
var spawnObs = Observable_1.Observable.create(function (subj) {
var stdin = opts.stdin, optsWithoutStdIn = __rest(opts, ["stdin"]);
var _a = findActualExecutable(exe, params), cmd = _a.cmd, args = _a.args;
d("spawning process: " + cmd + " " + args.join() + ", " + JSON.stringify(optsWithoutStdIn));
var origOpts = assign({}, optsWithoutStdIn);
if ('jobber' in origOpts) {
delete origOpts.jobber;
}
if ('split' in origOpts) {
delete origOpts.split;
}
;
var proc = spawnOg(cmd, args, origOpts);
var bufHandler = function (source) { return function (b) {
if (b.length < 1) {
return;
}
;
var chunk = '<< String sent back was too long >>';
try {
if (typeof b === 'string') {
chunk = b.toString();
}
else {
chunk = b.toString(origOpts.encoding || 'utf8');
}
}
catch (e) {
chunk = "<< Lost chunk of process output for " + exe + " - length was " + b.length + ">>";
}
subj.next({ source: source, text: chunk });
}; };
var ret = new Subscription_1.Subscription();
if (opts.stdin) {
if (proc.stdin) {
ret.add(opts.stdin.subscribe(function (x) { return proc.stdin.write(x); }, subj.error.bind(subj), function () { return proc.stdin.end(); }));
}
else {
subj.error(new Error("opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required"));
}
}
var stderrCompleted = null;
var stdoutCompleted = null;
var noClose = false;
if (proc.stdout) {
stdoutCompleted = new AsyncSubject_1.AsyncSubject();
proc.stdout.on('data', bufHandler('stdout'));
proc.stdout.on('close', function () { stdoutCompleted.next(true); stdoutCompleted.complete(); });
}
else {
stdoutCompleted = Observable_1.Observable.of(true);
}
if (proc.stderr) {
stderrCompleted = new AsyncSubject_1.AsyncSubject();
proc.stderr.on('data', bufHandler('stderr'));
proc.stderr.on('close', function () { stderrCompleted.next(true); stderrCompleted.complete(); });
}
else {
stderrCompleted = Observable_1.Observable.of(true);
}
proc.on('error', function (e) {
noClose = true;
subj.error(e);
});
proc.on('close', function (code) {
noClose = true;
var pipesClosed = Observable_1.Observable.merge(stdoutCompleted, stderrCompleted)
.reduce(function (acc) { return acc; }, true);
if (code === 0) {
pipesClosed.subscribe(function () { return subj.complete(); });
}
else {
pipesClosed.subscribe(function () { return subj.error(new Error("Failed with exit code: " + code)); });
}
});
ret.add(new Subscription_1.Subscription(function () {
if (noClose) {
return;
}
;
d("Killing process: " + cmd + " " + args.join());
if (opts.jobber) {
// NB: Connecting to Jobber's named pipe will kill it
net.connect("\\\\.\\pipe\\jobber-" + proc.pid);
setTimeout(function () { return proc.kill(); }, 5 * 1000);
}
else {
proc.kill();
}
}));
return ret;
});
return opts.split ? spawnObs : spawnObs.pluck('text');
}
exports.spawn = spawn;
function wrapObservableInPromise(obs) {
return new Promise(function (res, rej) {
var out = '';
obs.subscribe(function (x) { return out += x; }, function (e) { return rej(new Error(out + "\n" + e.message)); }, function () { return res(out); });
});
}
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a detached
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
function spawnDetachedPromise(exe, params, opts) {
if (opts === void 0) { opts = null; }
return wrapObservableInPromise(spawnDetached(exe, params, opts));
}
exports.spawnDetachedPromise = spawnDetachedPromise;
/**
* Spawns a process as a child process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a child
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
function spawnPromise(exe, params, opts) {
if (opts === void 0) { opts = null; }
return wrapObservableInPromise(spawn(exe, params, opts));
}
exports.spawnPromise = spawnPromise;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,38 @@
{
"name": "spawn-rx",
"version": "2.0.12",
"description": "An Rx-version of child_process.spawn",
"repository": {
"type": "git",
"url": "https://github.com/paulcbetts/spawn-rx"
},
"author": "Paul Betts <paul@paulbetts.org>",
"license": "MIT",
"main": "lib/src/index.js",
"typings": "lib/src/index.d.ts",
"homepage": "https://github.com/paulcbetts/spawn-rx",
"dependencies": {
"debug": "^2.5.1",
"lodash.assign": "^4.2.0",
"rxjs": "^5.1.1"
},
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/chai-as-promised": "^7.1.0",
"@types/mocha": "^2.2.39",
"@types/node": "^8.0.32",
"babel-register": "^6.23.0",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"esdoc": "^0.5.2",
"esdoc-es7-plugin": "0.0.3",
"esdoc-plugin-async-to-sync": "^0.5.0",
"marked": "^0.3.6",
"mocha": "^4.0.0",
"npm-run-all": "^4.0.2",
"ts-node": "^3.3.0",
"tslint": "^5.7.0",
"typescript": "^2.5.3",
"uuid": "3.0.1"
}
}

View File

@ -0,0 +1,341 @@
import * as path from 'path';
import * as net from 'net';
import * as sfs from 'fs';
import * as assign from 'lodash.assign';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/pluck';
import 'rxjs/add/operator/reduce';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { Subscription } from 'rxjs/Subscription';
import { AsyncSubject } from 'rxjs/AsyncSubject';
import { Subject } from 'rxjs/Subject';
import * as childProcess from 'child_process';
const spawnOg: typeof childProcess.spawn = require('child_process').spawn; //tslint:disable-line:no-var-requires
const isWindows = process.platform === 'win32';
const d = require('debug')('spawn-rx'); //tslint:disable-line:no-var-requires
/**
* stat a file but don't throw if it doesn't exist
*
* @param {string} file The path to a file
* @return {Stats} The stats structure
*
* @private
*/
function statSyncNoException(file: string): sfs.Stats | null {
try {
return sfs.statSync(file);
} catch (e) {
return null;
}
}
/**
* Search PATH to see if a file exists in any of the path folders.
*
* @param {string} exe The file to search for
* @return {string} A fully qualified path, or the original path if nothing
* is found
*
* @private
*/
function runDownPath(exe: string): string {
// NB: Windows won't search PATH looking for executables in spawn like
// Posix does
// Files with any directory path don't get this applied
if (exe.match(/[\\\/]/)) {
d('Path has slash in directory, bailing');
return exe;
}
let target = path.join('.', exe);
if (statSyncNoException(target)) {
d(`Found executable in currect directory: ${target}`);
return target;
}
let haystack = process.env.PATH!.split(isWindows ? ';' : ':');
for (let p of haystack) {
let needle = path.join(p, exe);
if (statSyncNoException(needle)) {
return needle;
};
}
d('Failed to find executable anywhere in path');
return exe;
}
/**
* Finds the actual executable and parameters to run on Windows. This method
* mimics the POSIX behavior of being able to run scripts as executables by
* replacing the passed-in executable with the script runner, for PowerShell,
* CMD, and node scripts.
*
* This method also does the work of running down PATH, which spawn on Windows
* also doesn't do, unlike on POSIX.
*
* @param {string} exe The executable to run
* @param {Array<string>} args The arguments to run
*
* @return {Object} The cmd and args to run
* @property {string} cmd The command to pass to spawn
* @property {Array<string>} args The arguments to pass to spawn
*/
export function findActualExecutable(exe: string, args: Array<string>): {
cmd: string;
args: Array<string>
} {
// POSIX can just execute scripts directly, no need for silly goosery
if (process.platform !== 'win32') {
return { cmd: runDownPath(exe), args: args };
}
if (!sfs.existsSync(exe)) {
// NB: When you write something like `surf-client ... -- surf-build` on Windows,
// a shell would normally convert that to surf-build.cmd, but since it's passed
// in as an argument, it doesn't happen
const possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];
for (let ext of possibleExts) {
let possibleFullPath = runDownPath(`${exe}${ext}`);
if (sfs.existsSync(possibleFullPath)) {
return findActualExecutable(possibleFullPath, args);
}
}
}
if (exe.match(/\.ps1$/i)) {
let cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
let psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
return { cmd: cmd, args: psargs.concat(args) };
}
if (exe.match(/\.(bat|cmd)$/i)) {
let cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'cmd.exe');
let cmdArgs = ['/C', exe, ...args];
return { cmd: cmd, args: cmdArgs };
}
if (exe.match(/\.(js)$/i)) {
let cmd = process.execPath;
let nodeArgs = [exe];
return { cmd: cmd, args: nodeArgs.concat(args) };
}
// Dunno lol
return { cmd: exe, args: args };
}
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group that can be killed by unsubscribing from the
* return Observable.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a detached process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
export function spawnDetached(exe: string, params: Array<string>, opts: any = null): Observable<string> {
const { cmd, args } = findActualExecutable(exe, params);
if (!isWindows) {
return spawn(cmd, args, assign({}, opts || {}, { detached: true }));
};
const newParams = [cmd].concat(args);
let target = path.join(__dirname, '..', '..', 'vendor', 'jobber', 'Jobber.exe');
let options = assign({}, opts || {}, { detached: true, jobber: true });
d(`spawnDetached: ${target}, ${newParams}`);
return spawn(target, newParams, options);
}
/**
* Spawns a process attached as a child of the current process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Observable<string>} Returns an Observable that when subscribed
* to, will create a child process. The
* process output will be streamed to this
* Observable, and if unsubscribed from, the
* process will be terminated early. If the
* process terminates with a non-zero value,
* the Observable will terminate with onError.
*/
export function spawn<T = string>(exe: string, params: Array<string> = [], opts: any = null): Observable<T> {
opts = opts || {};
let spawnObs = Observable.create((subj: Observer<{
source: any,
text: any
}>) => {
let { stdin, ...optsWithoutStdIn } = opts;
let { cmd, args } = findActualExecutable(exe, params);
d(`spawning process: ${cmd} ${args.join()}, ${JSON.stringify(optsWithoutStdIn)}`);
let origOpts = assign({}, optsWithoutStdIn);
if ('jobber' in origOpts) {
delete origOpts.jobber;
}
if ('split' in origOpts) {
delete origOpts.split;
};
const proc = spawnOg(cmd, args, origOpts);
let bufHandler = (source: string) => (b: string | Buffer) => {
if (b.length < 1) {
return;
};
let chunk = '<< String sent back was too long >>';
try {
if (typeof b === 'string') {
chunk = b.toString();
} else {
chunk = b.toString(origOpts.encoding || 'utf8');
}
} catch (e) {
chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
}
subj.next({ source: source, text: chunk });
};
let ret = new Subscription();
if (opts.stdin) {
if (proc.stdin) {
ret.add(opts.stdin.subscribe(
(x: any) => proc.stdin.write(x),
subj.error.bind(subj),
() => proc.stdin.end()
));
} else {
subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
}
}
let stderrCompleted: Subject<boolean> | Observable<boolean> | null = null;
let stdoutCompleted: Subject<boolean> | Observable<boolean> | null = null;
let noClose = false;
if (proc.stdout) {
stdoutCompleted = new AsyncSubject<boolean>();
proc.stdout.on('data', bufHandler('stdout'));
proc.stdout.on('close', () => { (stdoutCompleted! as Subject<boolean>).next(true); (stdoutCompleted! as Subject<boolean>).complete(); });
} else {
stdoutCompleted = Observable.of(true);
}
if (proc.stderr) {
stderrCompleted = new AsyncSubject<boolean>();
proc.stderr.on('data', bufHandler('stderr'));
proc.stderr.on('close', () => { (stderrCompleted! as Subject<boolean>).next(true); (stderrCompleted! as Subject<boolean>).complete(); });
} else {
stderrCompleted = Observable.of(true);
}
proc.on('error', (e: Error) => {
noClose = true;
subj.error(e);
});
proc.on('close', (code: number) => {
noClose = true;
let pipesClosed = Observable.merge(stdoutCompleted!, stderrCompleted!)
.reduce((acc) => acc, true);
if (code === 0) {
pipesClosed.subscribe(() => subj.complete());
} else {
pipesClosed.subscribe(() => subj.error(new Error(`Failed with exit code: ${code}`)));
}
});
ret.add(new Subscription(() => {
if (noClose) {
return;
};
d(`Killing process: ${cmd} ${args.join()}`);
if (opts.jobber) {
// NB: Connecting to Jobber's named pipe will kill it
net.connect(`\\\\.\\pipe\\jobber-${proc.pid}`);
setTimeout(() => proc.kill(), 5 * 1000);
} else {
proc.kill();
}
}));
return ret;
});
return opts.split ? spawnObs : spawnObs.pluck('text');
}
function wrapObservableInPromise<T>(obs: Observable<T>) {
return new Promise<string>((res, rej) => {
let out = '';
obs.subscribe(
(x) => out += x,
(e) => rej(new Error(`${out}\n${e.message}`)),
() => res(out));
});
}
/**
* Spawns a process but detached from the current process. The process is put
* into its own Process Group.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a detached
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
export function spawnDetachedPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
return wrapObservableInPromise<string>(spawnDetached(exe, params, opts));
}
/**
* Spawns a process as a child process.
*
* @param {string} exe The executable to run
* @param {Array<string>} params The parameters to pass to the child
* @param {Object} opts Options to pass to spawn.
*
* @return {Promise<string>} Returns an Promise that represents a child
* process. The value returned is the process
* output. If the process terminates with a
* non-zero value, the Promise will resolve with
* an Error.
*/
export function spawnPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
return wrapObservableInPromise<string>(spawn(exe, params, opts));
}

View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"removeComments": false,
"preserveConstEnums": true,
"sourceMap": true,
"declaration": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"suppressImplicitAnyIndexErrors": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"module": "commonjs",
"moduleResolution": "node",
"pretty": true,
"target": "es5",
"outDir": "lib",
"lib": ["dom", "es2015"]
},
"formatCodeOptions": {
"indentSize": 2,
"tabSize": 2
},
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,38 @@
{
"rules": {
"curly": true,
"eofline": false,
"align": [true, "parameters"],
"class-name": true,
"indent": [true, "spaces"],
"max-line-length": [true, 150],
"no-consecutive-blank-lines": [true],
"no-trailing-whitespace": true,
"no-duplicate-variable": true,
"no-var-keyword": true,
"no-empty": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-requires": true,
"one-line": [true,
"check-else",
"check-whitespace",
"check-open-brace"],
"quotemark": [true,
"single",
"avoid-escape"],
"semicolon": [true, "always"],
"typedef-whitespace": [true, {
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}],
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-type"]
}
}

Binary file not shown.