mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-06-11 08:04:23 +08:00
implement script to generate CodePushified React Native apps (#958)
* implement fully automatic script to generate CodePushified React Native apps * fix compatibility issues, remove generate-app.sh * update docs, improve react-native version detection
This commit is contained in:
178
Examples/create-app.js
Normal file
178
Examples/create-app.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
The script serves to generate CodePushified React Native app to reproduce issues or for testing purposes.
|
||||
|
||||
Requirements:
|
||||
1. npm i -g react-native-cli
|
||||
2. npm i -g code-push-cli
|
||||
3. code-push register
|
||||
|
||||
Usage: node create-app.js <appName> <reactNativeVersion> <reactNativeCodePushVersion>
|
||||
1. node create-app.js
|
||||
2. node create-app.js myapp
|
||||
3. node create-app.js myapp react-native@0.47.1 react-native-code-push@5.0.0-beta
|
||||
4. node create-app.js myapp react-native@latest Microsoft/react-native-code-push
|
||||
|
||||
Parameters:
|
||||
1. <appName> - CodePushDemoAppTest
|
||||
2. <reactNativeVersion> - react-native@latest
|
||||
3. <reactNativeCodePushVersion> - react-native-code-push@latest
|
||||
*/
|
||||
|
||||
let fs = require('fs');
|
||||
let path = require('path');
|
||||
let nexpect = require('./nexpect');
|
||||
let child_proces = require('child_process');
|
||||
let execSync = child_proces.execSync;
|
||||
|
||||
let args = process.argv.slice(2);
|
||||
let appName = args[0] || 'CodePushDemoAppTest';
|
||||
|
||||
if (fs.existsSync(appName)) {
|
||||
console.error(`Folder with name "${appName}" already exists! Please delete`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
let appNameAndroid = `${appName}-android`;
|
||||
let appNameIOS = `${appName}-ios`;
|
||||
let reactNativeVersion = args[1] || `react-native@${execSync('npm view react-native version')}`.trim();
|
||||
let reactNativeCodePushVersion = args[2] || `react-native-code-push@${execSync('npm view react-native-code-push version')}`.trim();
|
||||
|
||||
console.log(`App name: ${appName}`);
|
||||
console.log(`React Native version: ${reactNativeVersion}`);
|
||||
console.log(`React Native Module for CodePush version: ${reactNativeCodePushVersion} \n`);
|
||||
|
||||
let androidStagingDeploymentKey = null;
|
||||
let iosStagingDeploymentKey = null;
|
||||
|
||||
|
||||
|
||||
//GENERATE START
|
||||
createCodePushApp(appNameAndroid, 'android');
|
||||
createCodePushApp(appNameIOS, 'ios');
|
||||
|
||||
generatePlainReactNativeApp(appName, reactNativeVersion);
|
||||
process.chdir(appName);
|
||||
installCodePush(reactNativeCodePushVersion);
|
||||
linkCodePush(androidStagingDeploymentKey, iosStagingDeploymentKey);
|
||||
//GENERATE END
|
||||
|
||||
|
||||
|
||||
function createCodePushApp(name, platform) {
|
||||
try {
|
||||
console.log(`Creating CodePush app "${name}" to release updates for ${platform}...`);
|
||||
execSync(`code-push app add ${name} ${platform} react-native`);
|
||||
console.log(`App "${name}" has been created \n`);
|
||||
} catch (e) {
|
||||
console.log(`App "${name}" already exists \n`);
|
||||
}
|
||||
let deploymentKeys = JSON.parse(execSync(`code-push deployment ls ${name} -k --format json`));
|
||||
let stagingDeploymentKey = deploymentKeys[1].key;
|
||||
console.log(`Deployment key for ${platform}: ${stagingDeploymentKey}`);
|
||||
console.log(`Use "code-push release-react ${name} ${platform}" command to release updates for ${platform} \n`);
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
androidStagingDeploymentKey = stagingDeploymentKey;
|
||||
break;
|
||||
case 'ios':
|
||||
iosStagingDeploymentKey = stagingDeploymentKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function generatePlainReactNativeApp(appName, reactNativeVersion) {
|
||||
console.log(`Installing React Native...`);
|
||||
execSync(`react-native init ${appName} --version ${reactNativeVersion}`);
|
||||
console.log(`React Native has been installed \n`);
|
||||
}
|
||||
|
||||
function installCodePush(reactNativeCodePushVersion) {
|
||||
console.log(`Installing React Native Module for CodePush...`);
|
||||
execSync(`npm i --save ${reactNativeCodePushVersion}`);
|
||||
console.log(`React Native Module for CodePush has been installed \n`);
|
||||
}
|
||||
|
||||
function linkCodePush(androidStagingDeploymentKey, iosStagingDeploymentKey) {
|
||||
console.log(`Linking React Native Module for CodePush...`);
|
||||
nexpect.spawn(`react-native link react-native-code-push`)
|
||||
.wait("What is your CodePush deployment key for Android (hit <ENTER> to ignore)")
|
||||
.sendline(androidStagingDeploymentKey)
|
||||
.wait("What is your CodePush deployment key for iOS (hit <ENTER> to ignore)")
|
||||
.sendline(iosStagingDeploymentKey)
|
||||
.run(function (err) {
|
||||
if (!err) {
|
||||
console.log(`React Native Module for CodePush has been linked \n`);
|
||||
setupAssets();
|
||||
}
|
||||
else {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupAssets() {
|
||||
fs.unlinkSync('./index.ios.js');
|
||||
fs.unlinkSync('./index.android.js');
|
||||
|
||||
fs.writeFileSync('demo.js', fs.readFileSync('../CodePushDemoApp/demo.js'));
|
||||
fs.writeFileSync('index.ios.js', fs.readFileSync('../CodePushDemoApp/index.ios.js'));
|
||||
fs.writeFileSync('index.android.js', fs.readFileSync('../CodePushDemoApp/index.android.js'));
|
||||
|
||||
copyRecursiveSync('../CodePushDemoApp/images', './images');
|
||||
|
||||
fs.readFile('demo.js', 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
return console.error(err);
|
||||
}
|
||||
var result = data.replace(/CodePushDemoApp/g, appName);
|
||||
|
||||
fs.writeFile('demo.js', result, 'utf8', function (err) {
|
||||
if (err) return console.error(err);
|
||||
|
||||
if (!/^win/.test(process.platform)) {
|
||||
optimizeToTestInDebugMode();
|
||||
process.chdir('../');
|
||||
grantAccess(appName);
|
||||
}
|
||||
console.log(`\nReact Native app "${appName}" has been generated and CodePushified!`);
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function optimizeToTestInDebugMode() {
|
||||
let rnXcodeShLocationFolder = 'scripts';
|
||||
try {
|
||||
let rnVersions = JSON.parse(execSync(`npm view react-native versions --json`));
|
||||
let currentRNversion = JSON.parse(fs.readFileSync('./package.json'))['dependencies']['react-native'];
|
||||
if (rnVersions.indexOf(currentRNversion) > -1 &&
|
||||
rnVersions.indexOf(currentRNversion) < rnVersions.indexOf("0.46.0-rc.0")) {
|
||||
rnXcodeShLocationFolder = 'packager';
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
execSync(`perl -i -p0e 's/#ifdef DEBUG.*?#endif/jsCodeLocation = [CodePush bundleURL];/s' ios/${appName}/AppDelegate.m`);
|
||||
execSync(`sed -ie '17,20d' node_modules/react-native/${rnXcodeShLocationFolder}/react-native-xcode.sh`);
|
||||
execSync(`sed -ie 's/targetName.toLowerCase().contains("release")$/true/' node_modules/react-native/react.gradle`);
|
||||
}
|
||||
|
||||
function grantAccess(folderPath) {
|
||||
execSync('chown -R `whoami` ' + folderPath);
|
||||
execSync('chmod -R 755 ' + folderPath);
|
||||
}
|
||||
|
||||
function copyRecursiveSync(src, dest) {
|
||||
var exists = fs.existsSync(src);
|
||||
var stats = exists && fs.statSync(src);
|
||||
var isDirectory = exists && stats.isDirectory();
|
||||
if (exists && isDirectory) {
|
||||
fs.mkdirSync(dest);
|
||||
fs.readdirSync(src).forEach(function (childItemName) {
|
||||
copyRecursiveSync(path.join(src, childItemName),
|
||||
path.join(dest, childItemName));
|
||||
});
|
||||
} else {
|
||||
fs.linkSync(src, dest);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2015-present, Microsoft Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
echo 'CodePush + RN sample app generation script';
|
||||
echo
|
||||
|
||||
rm -rf testapp_rn
|
||||
|
||||
echo '************************ Configuration ***********************************';
|
||||
|
||||
#################### Configure versions #################################################
|
||||
|
||||
read -p "Enter React Native version (default: latest):" react_native_version
|
||||
read -p "Enter CodePush version (default: latest): " react_native_code_push_version
|
||||
|
||||
echo
|
||||
|
||||
if [ ! $react_native_version]; then
|
||||
react_native_version=`npm view react-native version`
|
||||
fi
|
||||
echo 'React Native version: ' + $react_native_version
|
||||
|
||||
if [ ! $react_native_code_push_version ]; then
|
||||
react_native_code_push_version=`npm view react-native-code-push version`
|
||||
fi
|
||||
echo 'React Native Code Push version: ' + $react_native_code_push_version
|
||||
echo
|
||||
|
||||
#################### Create app #########################################################
|
||||
|
||||
echo '********************* Creating app ***************************************';
|
||||
|
||||
current_dir=`pwd`;
|
||||
echo 'Current directory: ' + $current_dir;
|
||||
|
||||
echo 'Create testapp_rn app';
|
||||
rninit init testapp_rn --source react-native@$react_native_version
|
||||
|
||||
cd testapp_rn
|
||||
|
||||
echo 'Install React Native Code Push Version $react_native_code_push_version'
|
||||
npm install --save react-native-code-push@$react_native_code_push_version
|
||||
|
||||
echo 'react native link to react native code push'
|
||||
react-native link react-native-code-push
|
||||
|
||||
rm index.android.js
|
||||
rm index.ios.js
|
||||
cp ../CodePushDemoApp/*js .
|
||||
mkdir images
|
||||
cp ../CodePushDemoApp/images/* images
|
||||
|
||||
# Make changes required to test CodePush in debug mode (see OneNote)
|
||||
sed -ie '162s/AppRegistry.registerComponent("CodePushDemoApp", () => CodePushDemoApp);/AppRegistry.registerComponent("testapp_rn", () => CodePushDemoApp);/' demo.js
|
||||
perl -i -p0e 's/#ifdef DEBUG.*?#endif/jsCodeLocation = [CodePush bundleURL];/s' ios/testapp_rn/AppDelegate.m
|
||||
sed -ie '17,20d' node_modules/react-native/packager/react-native-xcode.sh
|
||||
sed -ie '90s/targetName.toLowerCase().contains("release")/true/' node_modules/react-native/react.gradle
|
||||
393
Examples/nexpect.js
Normal file
393
Examples/nexpect.js
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* nexpect.js: Top-level include for the `nexpect` module.
|
||||
*
|
||||
* (C) 2011, Elijah Insua, Marak Squires, Charlie Robbins.
|
||||
*
|
||||
*/
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var util = require('util');
|
||||
var AssertionError = require('assert').AssertionError;
|
||||
|
||||
function chain (context) {
|
||||
return {
|
||||
expect: function (expectation) {
|
||||
var _expect = function _expect (data) {
|
||||
return testExpectation(data, expectation);
|
||||
};
|
||||
|
||||
_expect.shift = true;
|
||||
_expect.expectation = expectation;
|
||||
_expect.description = '[expect] ' + expectation;
|
||||
_expect.requiresInput = true;
|
||||
context.queue.push(_expect);
|
||||
|
||||
return chain(context);
|
||||
},
|
||||
wait: function (expectation, callback) {
|
||||
var _wait = function _wait (data) {
|
||||
var val = testExpectation(data, expectation);
|
||||
if (val === true && typeof callback === 'function') {
|
||||
callback(data);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
_wait.shift = false;
|
||||
_wait.expectation = expectation;
|
||||
_wait.description = '[wait] ' + expectation;
|
||||
_wait.requiresInput = true;
|
||||
context.queue.push(_wait);
|
||||
return chain(context);
|
||||
},
|
||||
sendline: function (line) {
|
||||
var _sendline = function _sendline () {
|
||||
context.process.stdin.write(line + '\n');
|
||||
|
||||
if (context.verbose) {
|
||||
process.stdout.write(line + '\n');
|
||||
}
|
||||
};
|
||||
|
||||
_sendline.shift = true;
|
||||
_sendline.description = '[sendline] ' + line;
|
||||
_sendline.requiresInput = false;
|
||||
context.queue.push(_sendline);
|
||||
return chain(context);
|
||||
},
|
||||
sendEof: function() {
|
||||
var _sendEof = function _sendEof () {
|
||||
context.process.stdin.destroy();
|
||||
};
|
||||
_sendEof.shift = true;
|
||||
_sendEof.description = '[sendEof]';
|
||||
_sendEof.requiresInput = false;
|
||||
context.queue.push(_sendEof);
|
||||
return chain(context);
|
||||
},
|
||||
run: function (callback) {
|
||||
var errState = null,
|
||||
responded = false,
|
||||
stdout = [],
|
||||
options;
|
||||
|
||||
//
|
||||
// **onError**
|
||||
//
|
||||
// Helper function to respond to the callback with a
|
||||
// specified error. Kills the child process if necessary.
|
||||
//
|
||||
function onError (err, kill) {
|
||||
if (errState || responded) {
|
||||
return;
|
||||
}
|
||||
|
||||
errState = err;
|
||||
responded = true;
|
||||
|
||||
if (kill) {
|
||||
try { context.process.kill(); }
|
||||
catch (ex) { }
|
||||
}
|
||||
|
||||
callback(err);
|
||||
}
|
||||
|
||||
//
|
||||
// **validateFnType**
|
||||
//
|
||||
// Helper function to validate the `currentFn` in the
|
||||
// `context.queue` for the target chain.
|
||||
//
|
||||
function validateFnType (currentFn) {
|
||||
if (typeof currentFn !== 'function') {
|
||||
//
|
||||
// If the `currentFn` is not a function, short-circuit with an error.
|
||||
//
|
||||
onError(new Error('Cannot process non-function on nexpect stack.'), true);
|
||||
return false;
|
||||
}
|
||||
else if (['_expect', '_sendline', '_wait', '_sendEof'].indexOf(currentFn.name) === -1) {
|
||||
//
|
||||
// If the `currentFn` is a function, but not those set by `.sendline()` or
|
||||
// `.expect()` then short-circuit with an error.
|
||||
//
|
||||
onError(new Error('Unexpected context function name: ' + currentFn.name), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// **evalContext**
|
||||
//
|
||||
// Core evaluation logic that evaluates the next function in
|
||||
// `context.queue` against the specified `data` where the last
|
||||
// function run had `name`.
|
||||
//
|
||||
function evalContext (data, name) {
|
||||
var currentFn = context.queue[0];
|
||||
|
||||
if (!currentFn || (name === '_expect' && currentFn.name === '_expect')) {
|
||||
//
|
||||
// If there is nothing left on the context or we are trying to
|
||||
// evaluate two consecutive `_expect` functions, return.
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFn.shift) {
|
||||
context.queue.shift();
|
||||
}
|
||||
|
||||
if (!validateFnType(currentFn)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFn.name === '_expect') {
|
||||
//
|
||||
// If this is an `_expect` function, then evaluate it and attempt
|
||||
// to evaluate the next function (in case it is a `_sendline` function).
|
||||
//
|
||||
return currentFn(data) === true ?
|
||||
evalContext(data, '_expect') :
|
||||
onError(createExpectationError(currentFn.expectation, data), true);
|
||||
}
|
||||
else if (currentFn.name === '_wait') {
|
||||
//
|
||||
// If this is a `_wait` function, then evaluate it and if it returns true,
|
||||
// then evaluate the function (in case it is a `_sendline` function).
|
||||
//
|
||||
if (currentFn(data) === true) {
|
||||
context.queue.shift();
|
||||
evalContext(data, '_expect');
|
||||
}
|
||||
}
|
||||
else {
|
||||
//
|
||||
// If the `currentFn` is any other function then evaluate it
|
||||
//
|
||||
currentFn();
|
||||
|
||||
// Evaluate the next function if it does not need input
|
||||
var nextFn = context.queue[0];
|
||||
if (nextFn && !nextFn.requiresInput)
|
||||
evalContext(data);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// **onLine**
|
||||
//
|
||||
// Preprocesses the `data` from `context.process` on the
|
||||
// specified `context.stream` and then evaluates the processed lines:
|
||||
//
|
||||
// 1. Stripping ANSI colors (if necessary)
|
||||
// 2. Removing case sensitivity (if necessary)
|
||||
// 3. Splitting `data` into multiple lines.
|
||||
//
|
||||
function onLine (data) {
|
||||
data = data.toString();
|
||||
|
||||
if (context.stripColors) {
|
||||
data = data.replace(/\u001b\[\d{0,2}m/g, '');
|
||||
}
|
||||
|
||||
if (context.ignoreCase) {
|
||||
data = data.toLowerCase();
|
||||
}
|
||||
|
||||
var lines = data.split('\n').filter(function (line) { return line.length > 0; });
|
||||
stdout = stdout.concat(lines);
|
||||
|
||||
while (lines.length > 0) {
|
||||
evalContext(lines.shift(), null);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// **flushQueue**
|
||||
//
|
||||
// Helper function which flushes any remaining functions from
|
||||
// `context.queue` and responds to the `callback` accordingly.
|
||||
//
|
||||
function flushQueue () {
|
||||
var remainingQueue = context.queue.slice(),
|
||||
currentFn = context.queue.shift(),
|
||||
lastLine = stdout[stdout.length - 1];
|
||||
|
||||
if (!lastLine) {
|
||||
onError(createUnexpectedEndError(
|
||||
'No data from child with non-empty queue.', remainingQueue));
|
||||
return false;
|
||||
}
|
||||
else if (context.queue.length > 0) {
|
||||
onError(createUnexpectedEndError(
|
||||
'Non-empty queue on spawn exit.', remainingQueue));
|
||||
return false;
|
||||
}
|
||||
else if (!validateFnType(currentFn)) {
|
||||
// onError was called
|
||||
return false;
|
||||
}
|
||||
else if (currentFn.name === '_sendline') {
|
||||
onError(new Error('Cannot call sendline after the process has exited'));
|
||||
return false;
|
||||
}
|
||||
else if (currentFn.name === '_wait' || currentFn.name === '_expect') {
|
||||
if (currentFn(lastLine) !== true) {
|
||||
onError(createExpectationError(currentFn.expectation, lastLine));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// **onData**
|
||||
//
|
||||
// Helper function for writing any data from a stream
|
||||
// to `process.stdout`.
|
||||
//
|
||||
function onData (data) {
|
||||
process.stdout.write(data);
|
||||
}
|
||||
|
||||
options = {
|
||||
cwd: context.cwd,
|
||||
env: context.env
|
||||
};
|
||||
|
||||
//
|
||||
// Spawn the child process and begin processing the target
|
||||
// stream for this chain.
|
||||
//
|
||||
if (!/^win/.test(process.platform)) {
|
||||
context.process = spawn(context.command, context.params, options);
|
||||
} else {
|
||||
context.process = spawn('cmd', ['/c', `${context.command}`].concat(context.params), options);
|
||||
}
|
||||
|
||||
if (context.verbose) {
|
||||
context.process.stdout.on('data', onData);
|
||||
context.process.stderr.on('data', onData);
|
||||
}
|
||||
|
||||
if (context.stream === 'all') {
|
||||
context.process.stdout.on('data', onLine);
|
||||
context.process.stderr.on('data', onLine);
|
||||
} else {
|
||||
context.process[context.stream].on('data', onLine);
|
||||
}
|
||||
|
||||
context.process.on('error', onError);
|
||||
|
||||
//
|
||||
// When the process exits, check the output `code` and `signal`,
|
||||
// flush `context.queue` (if necessary) and respond to the callback
|
||||
// appropriately.
|
||||
//
|
||||
context.process.on('close', function (code, signal) {
|
||||
if (code === 127) {
|
||||
// XXX(sam) Not how node works (anymore?), 127 is what /bin/sh returns,
|
||||
// but it appears node does not, or not in all conditions, blithely
|
||||
// return 127 to user, it emits an 'error' from the child_process.
|
||||
|
||||
//
|
||||
// If the response code is `127` then `context.command` was not found.
|
||||
//
|
||||
return onError(new Error('Command not found: ' + context.command));
|
||||
}
|
||||
else if (context.queue.length && !flushQueue()) {
|
||||
// if flushQueue returned false, onError was called
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, stdout, signal || code);
|
||||
});
|
||||
|
||||
return context.process;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function testExpectation(data, expectation) {
|
||||
if (util.isRegExp(expectation)) {
|
||||
return expectation.test(data);
|
||||
} else {
|
||||
return data.indexOf(expectation) > -1;
|
||||
}
|
||||
}
|
||||
|
||||
function createUnexpectedEndError(message, remainingQueue) {
|
||||
var desc = remainingQueue.map(function(it) { return it.description; });
|
||||
var msg = message + '\n' + desc.join('\n');
|
||||
return new AssertionError({
|
||||
message: msg,
|
||||
expected: [],
|
||||
actual: desc
|
||||
});
|
||||
}
|
||||
|
||||
function createExpectationError(expected, actual) {
|
||||
var expectation;
|
||||
if (util.isRegExp(expected))
|
||||
expectation = 'to match ' + expected;
|
||||
else
|
||||
expectation = 'to contain ' + JSON.stringify(expected);
|
||||
|
||||
var err = new AssertionError({
|
||||
message: util.format('expected %j %s', actual, expectation),
|
||||
actual: actual,
|
||||
expected: expected
|
||||
});
|
||||
return err;
|
||||
}
|
||||
|
||||
function nspawn (command, params, options) {
|
||||
if (arguments.length === 2) {
|
||||
if (Array.isArray(arguments[1])) {
|
||||
options = {};
|
||||
}
|
||||
else {
|
||||
options = arguments[1];
|
||||
params = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(command)) {
|
||||
params = command;
|
||||
command = params.shift();
|
||||
}
|
||||
else if (typeof command === 'string') {
|
||||
command = command.split(' ');
|
||||
params = params || command.slice(1);
|
||||
command = command[0];
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
context = {
|
||||
command: command,
|
||||
cwd: options.cwd || undefined,
|
||||
env: options.env || undefined,
|
||||
ignoreCase: options.ignoreCase,
|
||||
params: params,
|
||||
queue: [],
|
||||
stream: options.stream || 'stdout',
|
||||
stripColors: options.stripColors,
|
||||
verbose: options.verbose
|
||||
};
|
||||
|
||||
return chain(context);
|
||||
}
|
||||
|
||||
//
|
||||
// Export the core `nspawn` function as well as `nexpect.nspawn` for
|
||||
// backwards compatibility.
|
||||
//
|
||||
module.exports.spawn = nspawn;
|
||||
module.exports.nspawn = {
|
||||
spawn: nspawn
|
||||
};
|
||||
Reference in New Issue
Block a user