blob: e2edda6a6aae2c675ea20a449cd2a0f05c87a0ef [file] [log] [blame]
/*
* Copyright 2015-2016 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fs = require('fs'),
ok_ = require('./repl-messages').ok_,
tmp = require('tmp'),
open = require('open'),
path = require('path'),
spawn = require('child_process').spawn;
exports.debug = function debugNodeJS(message, ws, echoChamberNames, done, commandLineOptions, eventBus) {
try {
exports._debug(message, ws, echoChamberNames, done, commandLineOptions, eventBus);
} catch (e) {
console.error(e);
}
};
exports._debug = function debugNodeJS(message, ws, echoChamberNames, done, commandLineOptions, eventBus) {
var code = message.action.exec.code;
var r = new RegExp(/main[\s]*\([^\)]*\)/);
var startOfMethodBody = code.search(r);
if (startOfMethodBody >= 0) {
var paren = code.indexOf('{', startOfMethodBody);
code = code.substring(0, paren + 1) + '\n // This is your main method\n // Click continue, and you will stop here\n debugger;\n' + code.substring(paren + 1);
}
/* var bootstrap = '\n\n\nvar result = main.apply(undefined, ' + JSON.stringify([message.actualParameters || {}]) + ');';
// fire our echo chamber trigger when the code is done
bootstrap += '\n\nvar openwhisk = require(\'openwhisk\');\n';
bootstrap += 'ow = openwhisk({api: \'' + api.host + api.path + '\', api_key: \'' + message.key + '\', namespace: \'' + message.action.namespace + '\' });\n';
bootstrap += 'ow.triggers.invoke({ triggerName: \'' + echoChamberNames.trigger + '\', params: result });\n';*/
code += '\n\n//\n';
code += '// Welcome to the OpenWhisk debugger.\n';
code += '//\n';
code += '// To proceed with debugging, press the continue => button.\n';
code += '// The first breakpoint will be in your main method\n';
code += '//\n';
code += '\n\nvar bootstrap = require(\'debug-bootstrap\')(\'' + message.key + '\', \'' + message.action.namespace + '\', \'' + echoChamberNames.trigger + '\');\nbootstrap(main, ' + JSON.stringify(message.actualParameters || {}) + ');';
tmp.file(function onTempFileCreation(err, tmpFilePath, fd, tmpfileCleanupCallback) {
// console.log('TMP ' + tmpFilePath);
try {
fs.write(fd, code, 0, 'utf8', function onFileWriteCompletion(err, written, string) {
// we need to update the NODE_PATH env var, to add our local modules
var env = Object.assign({}, process.env);
env.NODE_PATH = path.join(process.cwd(), 'node_modules')
+ ':' + path.join(process.cwd(), 'lib')
+ ':' + path.join(process.cwd(), 'deps', 'nodejs6', 'node_modules');
function trySpawnWithBrowser(webPort, debugPort) {
var spawnOpts = {
cwd: process.cwd(),
// stdio: ['inherit', 'inherit', 'inherit'], // for debugging
env: env
};
var child = spawn(path.join('node_modules', '.bin', 'node-debug'),
['--cli',
'--debug-port', debugPort,
'--web-port', webPort,
tmpFilePath],
spawnOpts);
var child2;
var addrInUse = false;
child.stdout.on('data', function(data) {
if (!child2) {
var url = 'http://127.0.0.1:' + webPort + '/?port=' + debugPort;
child2 = open(url, 'Google Chrome');
console.log('');
console.log('');
console.log('\tVisit ' + url.underline.blue + ' in the ' + 'Chrome'.red + ' browser that just popped up');
console.log('\tClose that browser tab to complete your debugging session'.bold);
console.log('');
console.log('');
}
});
// for debugging the child invocation:
child.stderr.on('data', (message) => {
message = message.toString();
if (message.indexOf('EADDRINUSE') >= 0) {
//
// oops, we'll need to try another pair of
// ports. we'll do son in the on('exit')
// handler below
//
addrInUse = true;
} else if (message.indexOf('ResourceTree') < 0
&& message.indexOf('Assertion failed') < 0) {
//
// ignore some internal errors in node-inspector
//
console.error('stderr: ' + message);
}
});
function cleanUpSubprocesses(err, stdout, stderr) {
if (err) {
if (addrInUse) {
trySpawnWithBrowser(webPort + 1, debugPort + 1);
} else {
console.log('Error launching debugger', err);
}
}
if (!addrInUse) {
try { tmpfileCleanupCallback(); } catch (e) { }
ok_(done);
}
}
child.on('close', cleanUpSubprocesses);
} /* end of trySpawnWithBrowser */
function spawnWithCLI() {
var spawnOpts = {
cwd: process.cwd(),
stdio: ['inherit', 'inherit', 'inherit'],
env: env
};
try {
var child = spawn('node',
['debug', tmpFilePath],
spawnOpts);
child.on('exit', (code) => {
if (code !== 0) {
console.error('The debugger exited abnormally with code ' + code);
}
});
eventBus.on('invocation-done', () => child.kill());
child.on('close', () => {
try { tmpfileCleanupCallback(); } catch (e) { }
done(); // we don't need to "ok" here, as the invoker will do that for us
});
} catch (e) {
console.error('Error spawning debugger', e);
console.error(e.stack);
done();
}
}
if (commandLineOptions['use-cli-debugger']) {
spawnWithCLI();
} else {
trySpawnWithBrowser(8080, 5858);
}
});
} catch (e) {
console.error(e);
console.error(e.stack);
try { tmpfileCleanupCallback(); } catch (e) { }
done();
}
});
};