ionic
Version:
A tool for creating and developing Ionic Framework mobile apps.
981 lines (803 loc) • 29.7 kB
JavaScript
/* eslint-disable no-underscore-dangle */
require('colors');
var fs = require('fs');
var Q = require('q');
var path = require('path');
var connect = require('connect');
var finalhandler = require('finalhandler');
var http = require('http');
var serveStatic = require('serve-static');
var tinylr = require('tiny-lr-fork');
var lr = require('connect-livereload');
var globWatch = require('glob-watcher');
var request = require('request');
var proxyMiddleware = require('proxy-middleware');
var url = require('url');
var xml2js = require('xml2js');
var Utils = require('./utils');
var events = require('./events');
var ports = require('./ports');
var log = require('./logging').logger;
var Serve = module.exports;
var lrServer;
var runningServer;
var DEFAULT_HTTP_PORT = 8100;
var DEFAULT_LIVE_RELOAD_PORT = 35729;
var IONIC_LAB_URL = '/ionic-lab';
var IONIC_ANGULAR_URL = '/angular.min.js';
Serve.listenForServerCommands = function listenForServerCommands(options) {
var readline = require('readline');
process.on('SIGINT', function() {
process.exit();
});
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
if (process.platform === 'win32') {
rl.on('SIGINT', function() {
process.emit('SIGINT');
});
}
rl.setPrompt('ionic $ ');
rl.prompt();
rl.on('line', function(entry) {
if (entry === null) return;
var input = (entry + '').trim();
if (input === 'quit' || input === 'q') rl.close();
if (input === null) return;
input = (input + '').trim();
if (input === 'restart' || input === 'r') {
Serve._goToUrl('/?restart=' + Math.floor((Math.random() * 899999) + 100000), options);
} else if (input.indexOf('goto ') === 0 || input.indexOf('g ') === 0) {
var url = input.replace('goto ', '').replace('g ', '');
Serve._goToUrl(url, options);
} else if (input === 'consolelogs' || input === 'c') {
options.printConsoleLogs = !options.printConsoleLogs;
log.info('Console log output: ' + (options.printConsoleLogs ? 'enabled' : 'disabled'));
if (options.printConsoleLogs) {
events.removeAllListeners('consoleLog');
events.on('consoleLog', console.log);
} else {
events.removeAllListeners('consoleLog');
}
Serve._goToUrl('/?restart=' + Math.floor((Math.random() * 899999) + 100000), options);
} else if (input === 'serverlogs' || input === 's') {
options.printServerLogs = !options.printServerLogs;
log.info('Server log output: ' + (options.printServerLogs ? 'enabled' : 'disabled'));
} else if (input.match(/^go\([+\-]?[0-9]{1,9}\)$/)) {
Serve._goToHistory(input, options);
} else if (input === 'help' || input === 'h') {
Serve.printCommandTips();
} else if (input === 'clear' || input === 'clr') {
process.stdout.write('\u001b[2J\u001b[0;0H');
} else {
log.error('\nInvalid ionic server command');
Serve.printCommandTips();
}
// log.debug('input: ', input);
rl.prompt();
}).on('close', function() {
if (options.childProcess) {
log.info('Closing Gulp process');
options.childProcess.kill('SIGTERM');
}
process.exit(0);
});
};
// isIonicServer = true when serving www directory, false when live reload
Serve.checkPorts = function(isIonicServer, testPort, testHost, options) {
log.debug('Serve.checkports isIonicServer', isIonicServer, 'testPort:',
testPort, 'testHost', testHost, 'options:', options);
var q = Q.defer();
var message = [];
if (!isIonicServer) {
testHost = null;
}
log.debug('Testing port:', testPort, 'Testing host:', testHost);
ports.getPort({ port: testPort, host: testHost }, function(err, port) {
if (err) {
return q.reject(err);
}
if (port !== testPort) {
message = ['The port ', testPort, ' was taken on the host ', options.address,
' - using port ', port, ' instead'].join('');
log.warn(message);
if (isIonicServer) {
options.port = port;
} else {
options.liveReloadPort = port;
}
}
q.resolve();
});
return q.promise;
};
// argv should be parsed out and put into a nice hash before getting here.
Serve.loadSettings = function loadSettings(argv, project) {
var errorMessage;
if (!argv) {
errorMessage = 'Serve.loadSettings - You must pass proper arguments to load settings from';
throw new Error(errorMessage);
}
if (!project) {
errorMessage = 'Serve.loadSettings - You must pass a project object to pull out ' +
'project specific settings from';
throw new Error(errorMessage);
}
var options = {};
options.port = options.port || argv.port || argv.p || DEFAULT_HTTP_PORT;
options.liveReloadPort = options.liveReloadPort || argv.livereloadport || argv.r ||
argv['livereload-port'] || argv.i || DEFAULT_LIVE_RELOAD_PORT;
options.launchBrowser = !argv.nobrowser && !argv.b;
options.launchLab = options.launchBrowser && (argv.lab || argv.l);
options.runLivereload = !(argv.nolivereload || argv.d);
options.mockCordova = !(argv.nocordovamock || argv.m);
var noProxyFlag = argv.noproxy || argv.x || false;
var proxies = project.get('proxies') || [];
if (!noProxyFlag && proxies.length > 0) {
options.useProxy = true;
options.proxies = proxies;
} else {
options.useProxy = false;
options.proxies = [];
}
options.watchSass = project.get('sass') === true && !argv.nosass && !argv.n;
options.browser = argv.browser || argv.w || '';
options.browserOption = argv.browserOption || argv.o || '';
options.platform = argv.platform || argv.t || null;
// Check for default browser being specified
options.defaultBrowser = argv.defaultBrowser || argv.f || project.get('defaultBrowser');
if (options.defaultBrowser) {
project.set('defaultBrowser', options.defaultBrowser);
project.save();
}
options.browser = options.browser || options.defaultBrowser;
options.watchPatterns = project.get('watchPatterns') || ['www/**/*', '!www/lib/**/*', '!www/**/*.map'];
options.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c;
options.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s;
options.isAddressCmd = argv._[0].toLowerCase() === 'address';
options.documentRoot = project.get('documentRoot') || 'www';
options.createDocumentRoot = project.get('createDocumentRoot') || null;
options.contentSrc = path.join(options.documentRoot, Utils.getContentSrc(options.documentRoot));
if (argv.v2) {
options.livereloadOptions = {
livereload: path.resolve(path.join(__dirname, '/assets/livereload.js'))
};
options.v2 = true;
}
return options;
};
Serve.printCommandTips = function() {
log.info('Ionic server commands, enter:');
log.info(' restart'.cyan + ' or ' + 'r'.cyan + ' to restart the client app from the root');
log.info(' goto'.cyan + ' or ' + 'g'.cyan + ' and a url to have the app navigate to the given url');
log.info(' consolelogs'.cyan + ' or ' + 'c'.cyan + ' to enable/disable console log output');
log.info(' serverlogs'.cyan + ' or ' + 's'.cyan + ' to enable/disable server log output');
log.info(' quit'.cyan + ' or ' + 'q'.cyan + ' to shutdown the server and exit');
log.info('');
};
Serve.openBrowser = function openBrowser(options) {
if (options.launchLab || options.launchBrowser) {
var open = require('open');
var openUrl = options.launchLab ?
[Serve.host(options.address, options.port), IONIC_LAB_URL] :
[Serve.host(options.address, options.port)];
if (options.browserOption) {
openUrl.push(options.browserOption);
}
if (options.platform) {
openUrl.push('?ionicplatform=', options.platform);
}
try {
open(openUrl.join(''), options.browser);
} catch (ex) {
log.error('Error opening the browser - ', ex);
}
}
};
Serve.checkForDocumentRoot = function checkForDocumentRoot(options) {
if (!fs.existsSync(path.join(options.appDirectory, options.documentRoot))) {
if (options.createDocumentRoot) {
fs.mkdirSync(options.documentRoot);
} else {
log.info('"' + options.documentRoot + '" directory cannot be found. Please make ' +
'sure the working directory is an Ionic project.');
return false;
}
}
return true;
};
Serve.runLivereload = function runLivereload(options, app) {
var q = Q.defer();
log.debug('Running Live Reload with options', options);
try {
if (!options.runLivereload) {
q.resolve();
return q.promise;
}
// TODO - get file path with dir before it
// EX They pass in /Users/joshbavari/Development/testing/facebooker
// options.watchPatterns.forEach(function(watchPath, index) {
// absoluteWatchPaths.push(path.join(options.appDirectory, watchPath));
// });
// log.debug('Absolute watch paths:', absoluteWatchPaths);
var watcher = globWatch(options.watchPatterns);
watcher.on('change', function(evt) {
// TODO: Move prototype to Serve._changed
Serve._changed(evt.path, options);
});
var liveReloadPort = process.env.CONNECT_LIVE_RELOAD_PORT || options.liveReloadPort;
options.liveReloadServer = Serve.host(options.address, liveReloadPort);
lrServer = tinylr(options.livereloadOptions);
lrServer.listen(liveReloadPort, function(err) {
if (err) {
q.reject(err);
return Utils.fail('Unable to start live reload server:', err);
} else {
q.resolve(lrServer);
}
});
var connectLiveReload = lr({ port: liveReloadPort });
app.use(connectLiveReload);
} catch (ex) {
q.reject(ex);
log.error('Live Reload failed to start, error: %s', ex, {});
throw ex;
}
return q.promise;
};
Serve.setupProxy = function setupProxy(options, app) {
if (options.useProxy) {
for (var x = 0; x < options.proxies.length; x += 1) {
var proxy = options.proxies[x];
var opts = url.parse(proxy.proxyUrl);
if (proxy.proxyNoAgent) {
opts.agent = false;
}
opts.rejectUnauthorized = !(proxy.rejectUnauthorized === false);
app.use(proxy.path, proxyMiddleware(opts));
log.info('Proxy added:', proxy.path + ' => ' + url.format(proxy.proxyUrl));
}
}
};
Serve.startServer = function startServer(options, app) {
options.devServer = Serve.host(options.address, options.port);
log.debug('Starting server at host address - ' + options.devServer);
var rootDirectory = path.join(options.appDirectory, options.documentRoot);
// Serve up the www folder by default
if (options.printConsoleLogs) {
events.on('consoleLog', console.log);
} else {
events.removeAllListeners('consolelog');
}
var serve = serveStatic(rootDirectory);
// Create static server
var server = http.createServer(function(req, res) {
var done = finalhandler(req, res);
var urlParsed = url.parse(req.url, true);
var platformOverride = urlParsed.query && urlParsed.query.ionicplatform;
var platformUrl = getPlatformUrl(req);
if (options.mockCordova && platformUrl) {
var platformWWW = path.join(options.appDirectory, getPlatformWWW(req));
if (options.isPlatformServe) {
fs.readFile(path.resolve(path.join(platformWWW, platformUrl)), function(err, buf) {
res.setHeader('Content-Type', 'application/javascript');
if (err) {
res.end('// mocked cordova.js response to prevent 404 errors during development');
if (req.url === '/cordova.js') {
Serve.serverLog(req, '(mocked)', options);
} else {
Serve.serverLog(req, '(Error ' + platformWWW + ')', options);
}
} else {
Serve.serverLog(req, '(' + platformWWW + ')', options);
res.end(buf);
}
});
} else {
Serve.serverLog(req, '(mocked)', options);
res.setHeader('Content-Type', 'application/javascript');
res.end('// mocked cordova.js response to prevent 404 errors during development');
}
return;
}
if (options.printConsoleLogs && req.url === '/__ionic-cli/console') {
Serve.consoleLog(req);
res.end('');
return;
}
if (req.url === IONIC_LAB_URL) {
var previewTemplate = options.v2 ? 'v2preview.html' : 'preview.html';
// Serve the lab page with the given object with template data
function labServeFn(context) { // eslint-disable-line no-inner-declarations
fs.readFile(path.resolve(path.join(__dirname, 'assets/' + previewTemplate)), function(err, buf) {
var html;
if (err) {
res.end('404');
} else {
html = buf.toString('utf8');
html = html.replace('//INSERT_JSON_HERE', 'var BOOTSTRAP = ' + JSON.stringify(context || {}));
}
res.setHeader('Content-Type', 'text/html');
res.end(html);
});
}
// If the config.xml file exists, let's parse it for some nice features like
// showing the name of the app in the title
if (fs.existsSync('config.xml')) {
fs.readFile(path.resolve('config.xml'), function(err, buf) { // eslint-disable-line handle-callback-err
var xml = buf.toString('utf8');
xml2js.parseString(xml, function(err, result) { // eslint-disable-line handle-callback-err
labServeFn({
appName: result.widget.name[0]
});
});
});
} else {
labServeFn();
}
return;
}
if (req.url === IONIC_ANGULAR_URL) {
fs.readFile(path.resolve(path.join(__dirname, 'assets/angular.min.js')), function(err, buf) {
if (err) {
res.end('404');
}
var jsFile = buf.toString('utf8');
res.setHeader('Content-Type', 'application/javascript');
res.end(jsFile);
});
return;
}
if (req.url.split('?')[0] === '/') {
var contentPath = path.join(options.appDirectory, options.contentSrc);
fs.readFile(contentPath, 'utf8', function(err, buf) {
res.setHeader('Content-Type', 'text/html');
if (err) {
Serve.serverLog(req, 'ERROR!', options);
res.end(err.toString());
} else {
Serve.serverLog(req, '(' + options.contentSrc + ')', options);
var html = injectGoToScript(buf.toString('utf8'));
if (options.printConsoleLogs) {
html = injectConsoleLogScript(html);
}
if (platformOverride && !options.v2) {
html = injectPlatformScript(html, platformOverride);
}
res.end(html);
}
});
return;
}
// root www directory file
Serve.serverLog(req, null, options);
serve(req, res, done);
});
// Listen
app.use(server);
try {
runningServer = app.listen(options.port, options.address);
} catch (ex) {
Utils.fail('Failed to start the Ionic server: ', ex.message);
}
log.info('√ Running dev server: ', options.devServer.cyan);
};
Serve.start = function start(options) {
if (!options) {
throw 'You cannot serve without options.';
}
try {
var app = connect();
options.childProcess = null;
if (!Serve.checkForDocumentRoot(options)) {
return Q.reject('"' + options.documentRoot + '" directory cannot be found. Please make ' +
'sure the working directory is an Ionic project.');
}
return Q.when()
.then(function() {
if (options.useProxy) {
return Serve.setupProxy(options, app);
}
})
.then(function() {
return Serve.runLivereload(options, app);
})
.then(function() {
if (options.runLivereload) {
log.info('Running live reload server:', options.liveReloadServer.cyan);
}
log.info('Watching:', options.watchPatterns.join(', ').cyan);
return Serve.startServer(options, app);
})
.then(function() {
if (options.launchBrowser) {
Serve.openBrowser(options);
}
})
.catch(function(error) {
log.error('Ionic serve failed - error: %s', error, {});
});
} catch (e) {
var msg;
if (e && (e + '').indexOf('EMFILE') > -1) {
msg = (e + '\n') +
'The watch process has exceed the default number of files to keep open.\n' +
'You can change the default with the following command:\n\n' +
' ulimit -n 1000\n\n' +
'In the command above, it\'s setting the default to watch a max of 1000 files.\n\n';
} else {
msg = ('server start error: ' + e.stack);
}
log.error(msg);
throw msg;
}
};
Serve.showFinishedServeMessage = function showFinishedServeMessage(options) {
Serve.printCommandTips(options);
Serve.listenForServerCommands(options);
};
Serve.serverLog = function(req, msg, options) {
if (options.printServerLogs) {
var log = 'serve ';
log += (req.url.length > 60 ? req.url.substr(0, 57) + '...' : req.url);
if (msg) {
log += ' ' + msg;
}
var ua = (req.headers && req.headers['user-agent'] || '');
if (ua.indexOf('Android') > 0) {
log += ' Android'.small;
} else if (ua.indexOf('iPhone') > -1 || ua.indexOf('iPad') > -1 || ua.indexOf('iPod') > -1) {
log += ' iOS'.small;
} else if (ua.indexOf('Windows Phone') > -1) {
log += ' Windows Phone'.small;
}
events.emit('serverlog', log);
}
};
Serve.consoleLog = function(req) {
var body = '';
req.on('data', function(data) {
if (data) body += data;
});
req.on('end', function() {
if (!body) return;
try {
var log = JSON.parse(body);
var msg = log.index + ' ';
while (msg.length < 5) {
msg += ' ';
}
msg += ' ' + (log.ts + '').substr(7) + ' ';
msg += log.method;
while (msg.length < 24) {
msg += ' ';
}
var msgIndent = '';
while (msgIndent.length < msg.length) {
msgIndent += ' ';
}
if (log.method === 'dir' || log.method === 'table') {
var isFirstLine = true;
log.args.forEach(function(argObj) {
for (var objKey in argObj) {
if (isFirstLine) {
isFirstLine = false;
} else {
msg += '\n' + msgIndent;
}
msg += objKey + ': ';
try {
msg += (JSON.stringify(argObj[objKey], null, 1)).replace(/\n/g, '');
} catch (e) {
msg += argObj[objKey];
}
}
});
} else if (log.args.length) {
if (log.args.length === 2 && log.args[0] === '%o' && log.args[1] === '[object Object]') return;
msg += log.args.join(', ');
}
if (log.method === 'error' || log.method === 'exception') msg = msg.red;
else if (log.method === 'warn') msg = msg.yellow;
else if (log.method === 'info') msg = msg.green;
else if (log.method === 'debug') msg = msg.blue;
events.emit('consoleLog', msg);
} catch (e) {} // eslint-disable-line no-empty
});
};
function getPlatformUrl(req) {
if (req.url === '/cordova.js' || req.url === '/cordova_plugins.js' || req.url.indexOf('/plugins/') === 0) {
return req.url;
}
}
function getPlatformWWW(req) {
var platformPath = 'www';
if (req && req.headers && req.headers['user-agent']) {
var ua = req.headers['user-agent'].toLowerCase();
if (ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1 || ua.indexOf('ipod') > -1) {
platformPath = path.join('platforms', 'ios', 'www');
} else if (ua.indexOf('android') > -1) {
platformPath = path.join('platforms', 'android', 'assets', 'www');
}
}
return platformPath;
}
function injectConsoleLogScript(html) {
try {
var findTags = html.match(/<html(?=[\s>])(.*?)>|<head>|<meta charset(.*?)>/gi);
var insertAfter = findTags[findTags.length - 1];
/* eslint-disable max-len */
return html.replace(insertAfter, insertAfter + '\n\
<script>\n\
// Injected Ionic CLI Console Logger\n\
(function() {\n\
var methods = "assert clear count debug dir dirxml error exception group groupCollapsed groupEnd info log markTimeline profile profileEnd table time timeEnd timeStamp trace warn".split(" ");\n\
var console = (window.console=window.console || {});\n\
var logCount = 0;\n\
window.onerror = function(msg, url, line) {\n\
if (msg && url) console.error(msg, url, (line ? "Line: " + line : ""));\n\
};\n\
function sendConsoleLog(method, args) {\n\
try {\n\
var xhr = new XMLHttpRequest();\n\
xhr.open("POST", "/__ionic-cli/console", true);\n\
xhr.send(JSON.stringify({ index: logCount, method: method, ts: Date.now(), args: args }));\n\
logCount++;\n\
} catch(e){}\n\
}\n\
for(var x=0; x<methods.length; x++) {\n\
(function(m){\n\
var orgConsole = console[m];\n\
console[m] = function() {\n\
try {\n\
sendConsoleLog(m, Array.prototype.slice.call(arguments));\n\
if (orgConsole) orgConsole.apply(console, arguments);\n\
} catch(e){}\n\
};\n\
})(methods[x]);\n\
}\n\
}());\n\
</script>');
/* eslint-enable max-len */
} catch (e) {} // eslint-disable-line no-empty
return html;
}
function injectGoToScript(html) {
try {
var findTags = html.match(/<html(?=[\s>])(.*?)>|<head>|<meta charset(.*?)>/gi);
var insertAfter = findTags[findTags.length - 1];
return html.replace(insertAfter, insertAfter + '\n\
<script>\n\
// Injected Ionic Go To URL Live Reload Plugin\n\
window.LiveReloadPlugin_IonicGoToUrl = (function() {\n\
var GOTO_KEY = "__ionic_goto_url__";\n\
var HISTORY_GO_KEY = "__ionic_history_go__";\n\
var GoToUrlPlugin = function(window, host) {\n\
this.window = window;\n\
this.host = host;\n\
}\n\
GoToUrlPlugin.identifier = "__ionic_goto_url__";\n\
GoToUrlPlugin.version = "1.0";\n\
GoToUrlPlugin.prototype.reload = function(path) {\n\
try {\n\
if (path) {\n\
if (path.indexOf(GOTO_KEY) === 0) {\n\
this.window.document.location = path.replace(GOTO_KEY, "");\n\
return true;\n\
}\n\
if (path.indexOf(HISTORY_GO_KEY) === 0) {\n\
this.window.document.history.go( parseInt(path.replace(HISTORY_GO_KEY, ""), 10));\n\
return true;\n\
}\n\
}\n\
} catch(e) {\n\
console.log(e);\n\
}\n\
return false;\n\
};\n\
return GoToUrlPlugin;\n\
})();\n\
</script>');
} catch (e) {} // eslint-disable-line no-empty
return html;
}
/**
* Inject the platform override for choosing Android or iOS during
* development.
*/
function injectPlatformScript(html, platformOverride) {
try {
var findTags = html.toLowerCase().indexOf('</body>');
if (findTags < 0) { return html; }
return html.slice(0, findTags) + '\n' +
'<script>\n' +
'ionic && ionic.Platform && ionic.Platform.setPlatform("' + platformOverride + '");\n' +
'</script>\n' +
html.slice(findTags);
} catch (e) {} // eslint-disable-line no-empty
return html;
}
Serve._changed = function(filePath, options) {
// Cleanup the path a bit
// var pwd = process.cwd();
var pwd = path.join(options.appDirectory);
filePath = filePath.replace(pwd + '/', '');
if (filePath.indexOf('.css') > 0) {
log.info('CSS changed: ' + filePath);
} else if (filePath.indexOf('.js') > 0) {
log.info('JS changed: ' + filePath);
} else if (filePath.indexOf('.html') > 0) {
log.info('HTML changed: ' + filePath);
} else {
log.info('File changed: ' + filePath);
}
Serve._postToLiveReload([filePath], options);
};
Serve._goToUrl = function(url, options) {
log.info('Loading: ' + url);
Serve._postToLiveReload(['__ionic_goto_url__' + url], options);
};
Serve._goToHistory = function(goHistory, options) {
goHistory = goHistory.replace('go(', '').replace(')', '');
log.info('History Go: ' + goHistory);
Serve._postToLiveReload(['__ionic_history_go__' + goHistory], options);
};
Serve._postToLiveReload = function(files, options) {
var postUrl = [options.liveReloadServer, '/changed'].join('');
request.post(postUrl, {
path: '/changed',
method: 'POST',
body: JSON.stringify({
files: files
})
}, function(err) {
if (err) {
log.error('Unable to update live reload: %s', err, {});
}
});
};
Serve.getAddress = function(options) {
var q = Q.defer();
try {
var addresses = [];
var os = require('os');
var ifaces = os.networkInterfaces();
var ionicConfig = require('./config').load();
var addressConfigKey = (options.isPlatformServe ? 'platformServeAddress' : 'ionicServeAddress');
var tryAddress;
if (options.isAddressCmd) {
// reset any address configs
ionicConfig.set('ionicServeAddress', null);
ionicConfig.set('platformServeAddress', null);
} else if (!options.address) {
tryAddress = ionicConfig.get(addressConfigKey);
} else {
tryAddress = options.address;
}
if (ifaces) {
for (var dev in ifaces) {
if (!dev) continue;
ifaces[dev].forEach(function(details) {
if (details && details.family === 'IPv4' && !details.internal && details.address) {
addresses.push({
address: details.address,
dev: dev
});
}
});
}
}
if (tryAddress) {
if (tryAddress === 'localhost') {
options.address = tryAddress;
q.resolve();
return q.promise;
}
for (var x = 0; x < addresses.length; x += 1) {
// double check if this address is still available
if (addresses[x].address === tryAddress) {
options.address = addresses[x].address;
q.resolve();
return q.promise;
}
}
if (options.address) {
Utils.fail('Address ' + options.address + ' not available.');
return q.promise;
}
}
if (addresses.length > 0) {
if (!options.isPlatformServe) {
addresses.push({
address: 'localhost'
});
}
if (addresses.length === 1) {
options.address = addresses[0].address;
q.resolve();
return q.promise;
}
log.info('\nMultiple addresses available.');
log.info('Please select which address to use by entering its number from the list below:');
if (options.isPlatformServe) {
log.info('Note that the emulator/device must be able to access the given IP address');
}
for (var y = 0; y < addresses.length; y += 1) {
log.info((' ' + (y + 1) + ') ' + addresses[y].address +
(addresses[y].dev ?
' (' + addresses[y].dev + ')' :
'')));
}
var prompt = require('prompt');
var promptProperties = {
selection: {
name: 'selection',
description: 'Address Selection: ',
required: true
}
};
// prompt.override = argv;
prompt.message = '';
prompt.delimiter = '';
prompt.colors = false;
prompt.start();
prompt.get({ properties: promptProperties }, function(err, promptResult) {
if (err && err.message !== 'canceled') {
log.debug('User prompted to select address - an error occured: %s', err, {});
q.reject(err);
return log.error(err);
}
if (err && err.message === 'canceled') {
log.info('Canceled by user');
process.exit(1);
}
var selection = promptResult.selection;
for (var x = 0; x < addresses.length; x += 1) {
if (parseInt(selection) === (x + 1) || selection === addresses[x].address || selection === addresses[x].dev) {
options.address = addresses[x].address;
if (!options.isAddressCmd) {
log.info('Selected address: ' + options.address);
}
ionicConfig.set(addressConfigKey, options.address);
prompt.resume();
q.resolve();
return q.promise;
}
}
Utils.fail('Invalid address selection');
});
} else if (options.isPlatformServe) {
// no addresses found
Utils.fail('Unable to find an IPv4 address for run/emulate live reload.\nIs WiFi disabled or LAN disconnected?');
} else {
// no address found, but doesn't matter if it doesn't need an ip address and localhost will do
options.address = 'localhost';
q.resolve();
}
} catch (e) {
Utils.fail('Error getting IPv4 address: ' + e);
}
return q.promise;
};
Serve.host = function host(address, port) {
var platformName = require('os').platform();
if ((platformName.indexOf('win') !== -1 && platformName !== 'darwin') &&
(address === '0.0.0.0' || address.indexOf('0.0.0.0') !== -1)) {
// Windows doesnt understand 0.0.0.0 - direct to localhost instead
address = 'localhost';
}
var hostAddress = ['http://', address, ':', port].join('');
return hostAddress;
};
Serve.stopServer = function stopServer() {
if (runningServer) {
runningServer.close();
lrServer.close();
log.info('Server closed');
} else {
log.info('Server not running');
}
};