From 24a4ed9bdcfae8a049ea0c1bd02d27ce39d3f2cb Mon Sep 17 00:00:00 2001 From: ekatek Date: Thu, 4 Dec 2014 17:56:04 -0800 Subject: [PATCH 01/16] Automatically line-wrap output Includes the following changes to Console.js: - Console.info, Console.warn, Console.debug and Console.error now automatically line-wrap the output to 80 characters, or the width of the terminal screen (if known). This is in line with our current style guide on how things should be wrapped! - Sometimes, there are parts of text that we don't want to line-wrap. For example, if we are telling the user to run 'meteor long-command --with --options' we don't want to have a newline in the middle of that! Wrap those commands in Console.command, like this: Console.info("something and then run", Console.command(command), "and then"); This also makes them bold if chalk is on, as a nice bonus. So, if we ever turn chalk back on, the bolding of commands will be more consistent. - Sometimes, there is bulkier output that we don't want to format at all, including line-wrapping: log snippets, stack traces, JSON output, etc. In that case, we can use Console.rawInfo, Console.rawError, Console.rawWarn and Console.rawDebug. Don't use Console.command inside the raw* functions! It won't be processed (at all). - There are fancier things that we can do, other than just simply wrapping things. We can indent: " Start here and then when wrapping continue over here". We frequently do this for commands, for example. In the past, we did this manually -- but we can't do this for long messages that might get wrapped, and anyway, it is good to codify this instead of counting spaces. Allows us to be better about consistency, for example. - We can also add a bulletPoint, which is a small notice in the beginning that looks like this: " => Start here and then when wrapping continue below the bulletPoint". Since it is a elss intuitive option, I have wrapped most of the time that we use a bulletPoint into helper functions on the Console.js. - Some common bulletpoints that we use are: ASCII Checkboxes (Console.success) ASCII X-s (Console.failWarn and Console.failInfo) => (Console.arrowError, Console.arrowWarn, Console.arrowInfo) WARNING (Console.labelWarn) The => are sometimes indented, so they take an optional indent argument, showing how many spaces to indent by. The wrapper interface would be less complicated, if there was a more unified conceit behind our terminal messages. If there is one, it is not documented. My hope is that, in many cases, moving these to Console will make it easier for someone with great product sense to clean up our terminal messages. It will also make it easier to write such messages, since it will be easier to follow an accepted standard. In the codebase outside of Console: - Went through and looked at our use of Console.error/info/etc, replacing with rawError/etc whenever approporiate. - Went through and modified most of 'stdout' and 'stderr' calls to use the new functions. I made an exception for stuff that doesn't want a new line at the end, or otherwise does weird things (ex: print user logs directly), on the basis that, at this juncture, it is better to be safe than to be sorry. - Long messages no longer need to break the code style guide by ignoring indentation rules. Fixed that where approporiate. - Fixed the tests! A number of our stock messages are actually longer than 80 chars. - Personal favourite: The Android license agreement is now line wrapped! Much better experience. - There is some more work to do on: - longform help (currently comes with built-in linebreaks, would have to change the entire mechanism for how that works) - Buildmessage sometimes has headers that start with =>, but they are short. I didn't want to pass wrapper options all the way to main.captureOrExit before merging the rest of this and making sure that we like it. Since these messages are fairly short, I don't think that's likely to be a serious problem. I hope that this makes life easier for us in the future! No more counting chars, no more breaking the style guide. Better experience for users with wider terminals (or even shorter terminals!). Let's give this a try. --- meteor | 2 +- scripts/dev-bundle-tool-package.js | 1 + scripts/generate-dev-bundle.sh | 2 +- tools/auth-client.js | 15 +- tools/auth.js | 118 ++++---- tools/commands-cordova.js | 353 +++++++++++++---------- tools/commands-packages.js | 332 ++++++++++++--------- tools/commands.js | 342 ++++++++++++---------- tools/console.js | 448 ++++++++++++++++++++++++----- tools/deploy-galaxy.js | 31 +- tools/deploy.js | 199 +++++++------ tools/isopack.js | 5 +- tools/isopackets.js | 2 +- tools/main.js | 177 +++++++----- tools/run-all.js | 54 ++-- tools/run-app.js | 21 +- tools/run-log.js | 22 +- tools/run-velocity.js | 12 +- tools/selftest.js | 67 ++--- tools/stats.js | 12 +- tools/tests/package-tests.js | 2 +- tools/tests/releases.js | 6 +- tools/utils.js | 48 +--- 23 files changed, 1377 insertions(+), 894 deletions(-) diff --git a/meteor b/meteor index fb7b13b350..5b2f7ed21d 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/bin/bash -BUNDLE_VERSION=0.3.72 # 0.3.63 on the Windows branch +BUNDLE_VERSION=0.3.74 # 0.3.63 on the Windows branch # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index c3bcdb53fb..109d0a1c76 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -33,6 +33,7 @@ var packageJson = { netroute: "0.2.5", phantomjs: "1.9.12", "http-proxy": "1.6.0", + "wordwrap": "0.0.2", // XXX We ought to be able to get this from the copy in js-analyze rather // than in the dev bundle.) esprima: "1.2.2", diff --git a/scripts/generate-dev-bundle.sh b/scripts/generate-dev-bundle.sh index 0ddadb27a0..f67f143106 100755 --- a/scripts/generate-dev-bundle.sh +++ b/scripts/generate-dev-bundle.sh @@ -117,7 +117,6 @@ npm install npm dedupe cp -R node_modules/* "${DIR}/lib/node_modules/" - cd "${DIR}/lib" # TODO Move this into dev-bundle-tool-package.js when it can be safely @@ -142,6 +141,7 @@ delete browserstack-webdriver/docs delete browserstack-webdriver/lib/test delete sqlite3/deps +delete wordwrap/test # dedupe isn't good enough to eliminate 3 copies of esprima, sigh. find . -path '*/esprima/test' | xargs rm -rf diff --git a/tools/auth-client.js b/tools/auth-client.js index bc9e9ac664..60d775a164 100644 --- a/tools/auth-client.js +++ b/tools/auth-client.js @@ -58,9 +58,10 @@ exports.loggedInConnection = function (url, domain, sessionType) { if (! auth.isLoggedIn()) { // XXX we should have a better account signup page. - Console.stderr.write( -"Please log in with your Meteor developer account. If you don't have one,\n" + -"you can quickly create one at www.meteor.com.\n"); + Console.error( + "Please log in with your Meteor developer account.", + "If you don't have one,", + "you can quickly create one at www.meteor.com."); auth.doUsernamePasswordLogin({ retry: true }); } @@ -78,10 +79,10 @@ exports.loggedInConnection = function (url, domain, sessionType) { if (err.message === "access-denied") { // Maybe we thought we were logged in, but our token had been // revoked. - Console.stderr.write( -"It looks like you have been logged out! Please log in with your Meteor\n" + -"developer account. If you don't have one, you can quickly create one\n" + -"at www.meteor.com.\n"); + Console.error( + "It looks like you have been logged out!", + "Please log in with your Meteor developer account. If you don't have", + "one, you can quickly create one at www.meteor.com."); auth.doUsernamePasswordLogin({ retry: true }); auth.loginWithTokenOrOAuth( conn, diff --git a/tools/auth.js b/tools/auth.js index 43b1b7a456..249c6e8ffe 100644 --- a/tools/auth.js +++ b/tools/auth.js @@ -336,11 +336,10 @@ var tryRevokeOldTokens = function (options) { var logoutFailWarning = function (domain) { if (! warned) { // This isn't ideal but is probably better that saying nothing at all - process.stderr.write("warning: " + - (options.firstTry ? - "couldn't" : "still trying to") + - " confirm logout with " + domain + - "\n"); + Console.error("warning: " + + (options.firstTry ? + "couldn't" : "still trying to") + + " confirm logout with " + domain); warned = true; } }; @@ -603,7 +602,7 @@ var doInteractivePasswordLogin = function (options) { var loginFailed = function () { if (! options.suppressErrorMessage) { - process.stderr.write("Login failed.\n"); + Console.error("Login failed."); } }; @@ -634,7 +633,7 @@ var doInteractivePasswordLogin = function (options) { } else { loginFailed(); if (options.retry) { - process.stderr.write("\n"); + Console.error(); continue; } else { maybeCloseConnection(); @@ -715,17 +714,18 @@ exports.loginCommand = withAccountsConnection(function (options, var galaxyLoginResult = logInToGalaxy(galaxy); if (galaxyLoginResult.error) { // XXX add human readable error messages - process.stderr.write('\nLogin to ' + galaxy + ' failed. '); - + var failedLoginMsg = "\nLogin to ' + galaxy + ' failed. "; if (galaxyLoginResult.error === 'unauthorized') { - process.stderr.write('You are not authorized for this galaxy.\n'); + Console.error( + failedLoginMsg + 'You are not authorized for this galaxy.'); } else if (galaxyLoginResult.error === 'no_oauth_server') { - process.stderr.write('The galaxy could not ' + - 'contact Meteor Accounts.\n'); + Console.error( + failedLoginMsg + 'The galaxy could not contact Meteor Accounts.'); } else if (galaxyLoginResult.error === 'no_identity') { - process.stderr.write('Your login information could not be found.\n'); + Console.error( + failedLoginMsg + 'Your login information could not be found.'); } else { - process.stderr.write('Error: ' + galaxyLoginResult.error + '\n'); + Console.error(failedLoginMsg + 'Error: ' + galaxyLoginResult.error ); } return 1; @@ -741,10 +741,11 @@ exports.loginCommand = withAccountsConnection(function (options, tryRevokeOldTokens({ firstTry: true, connection: connection }); data = readSessionData(); - process.stderr.write("\nLogged in" + (galaxy ? " to " + galaxy : "") + - (currentUsername(data) ? - " as " + currentUsername(data) : "") + ".\n" + - "Thanks for being a Meteor developer!\n"); + Console.error(); + Console.error("Logged in" + (galaxy ? " to " + galaxy : "") + + (currentUsername(data) ? + " as " + currentUsername(data) : "") + "." + + "Thanks for being a Meteor developer!"); return 0; }); @@ -759,11 +760,11 @@ exports.logoutCommand = function (options) { tryRevokeOldTokens({ firstTry: true }); if (wasLoggedIn) - process.stderr.write("Logged out.\n"); + Console.error("Logged out."); else // We called logOutAllSessions/writeSessionData anyway, out of an // abundance of caution. - process.stderr.write("Not logged in.\n"); + Console.error("Not logged in."); }; // If this is fully set up account (with a username and password), or @@ -845,25 +846,25 @@ exports.whoAmICommand = function (options) { var data = readSessionData(); if (! loggedIn(data)) { - process.stderr.write("Not logged in. 'meteor login' to log in.\n"); + Console.error( + "Not logged in. " + Console.command("'meteor login'") + " to log in."); return 1; } var username = currentUsername(data); if (username) { - process.stdout.write(username + "\n"); + Console.info(Console.command(username)); return 0; } var url = getSession(data, config.getAccountsDomain()).registrationUrl; if (url) { - process.stderr.write( -"You haven't chosen your username yet. To pick it, go here:\n" + -"\n" + -url + "\n"); + Console.error("You haven't chosen your username yet. To pick it, go here:"); + Console.error(); + Console.error(url); } else { // Won't happen in normal operation - process.stderr.write("You haven't chosen your username yet.\n"); + Console.error("You haven't chosen your username yet."); } return 1; @@ -893,11 +894,13 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { break; } catch (err) { if (err.error === 400 && ! utils.validEmail(email)) { - if (email.trim().length) - process.stderr.write("Please double-check that address.\n\n"); + if (email.trim().length) { + Console.error("Please double-check that address."); + Console.error(); + } } else { - process.stderr.write("\nCouldn't connect to server. " + - "Check your internet connection.\n"); + Console.error("\nCouldn't connect to server. " + + "Check your internet connection."); return false; } } @@ -917,10 +920,11 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { writeSessionData(data); return true; } else if (result.alreadyExisted && result.sentRegistrationEmail) { - process.stderr.write( -"\n" + -"You need to pick a password for your account so that you can log in.\n" + -"An email has been sent to you with the link.\n\n"); + Console.error(); + Console.error( + "You need to pick a password for your account so that you can log in.", + "An email has been sent to you with the link."); + Console.error(); var animationFrame = 0; var lastLinePrinted = ""; @@ -928,11 +932,11 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { var spinner = ['-', '\\', '|', '/']; lastLinePrinted = "Waiting for you to register on the web... " + spinner[animationFrame]; - process.stderr.write(lastLinePrinted + "\r"); + Console.error(lastLinePrinted + "\r"); animationFrame = (animationFrame + 1) % spinner.length; }, 200); var stopSpinner = function () { - process.stderr.write(new Array(lastLinePrinted.length + 1).join(' ') + + Console.stderr.write(new Array(lastLinePrinted.length + 1).join(' ') + "\r"); clearInterval(timer); }; @@ -946,14 +950,14 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { stopSpinner(); if (e.errorType !== "Meteor.Error") throw e; - process.stderr.write( - "When you've picked your password, run 'meteor login' to log in.\n") + Console.error( + "When you've picked your password, run " + + Console.command("'meteor login'") + " to log in."); return false; } stopSpinner(); - process.stderr.write("Username: " + - waitForRegistrationResult.username + "\n"); + Console.error("Username: " + waitForRegistrationResult.username); loginResult = doInteractivePasswordLogin({ username: waitForRegistrationResult.username, retry: true, @@ -961,7 +965,7 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { }); return loginResult; } else if (result.alreadyExisted && result.username) { - process.stderr.write("\nLogging in as " + result.username + ".\n"); + Console.error("\nLogging in as " + Console.command(result.username) + "."); loginResult = doInteractivePasswordLogin({ username: result.username, @@ -971,8 +975,9 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { return loginResult; } else { // Hmm, got an email we don't understand. - process.stderr.write( - "\nThere was a problem. Please log in with 'meteor login'.\n"); + Console.error( + "\nThere was a problem. Please log in with " + + Console.command("'meteor login'") + "."); return false; } }); @@ -989,26 +994,29 @@ exports.maybePrintRegistrationLink = function (options) { if (session.userId && ! session.username && session.registrationUrl) { if (options.leadingNewline) - process.stderr.write("\n"); + Console.error(); if (options.onlyAllowIfRegistered) { // A stronger message: we're going to not allow whatever they were trying // to do! - process.stderr.write( -"You need to claim a username and set a password on your Meteor developer\n" + -"account to run this command. It takes about a minute at:\n" + -" " + session.registrationUrl + "\n"); + Console.error( + "You need to claim a username and set a password on your Meteor", + "developer account to run this command. It takes about a minute at:", + session.registrationUrl); + Console.error(); } else if (! options.firstTime) { // If they've already been prompted to set a password then this // is more of a friendly reminder, so we word it slightly // differently than the first time they're being shown a // registration url. - process.stderr.write( -"You should set a password on your Meteor developer account. It takes\n" + -"about a minute at: " + session.registrationUrl + "\n\n"); + Console.error( + "You should set a password on your Meteor developer account.", + "It takes about a minute at:", session.registrationUrl); + Console.error(); } else { - process.stderr.write( -"You can set a password on your account or change your email address at:\n" + -session.registrationUrl + "\n\n"); + Console.error( + "You can set a password on your account or change your email", + "address at:" + session.registrationUrl); + Console.error(); } return true; } diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index f2f16fe7c1..ed091dcd61 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -90,17 +90,17 @@ cordova.buildTargets = function (projectContext, targets, options) { if (! inProject) { if (! supported) { - Console.warn(Console.fail(MESSAGE_IOS_ONLY_ON_MAC)); + Console.failWarn(MESSAGE_IOS_ONLY_ON_MAC); } else { Console.warn("Please add the " + displayPlatform + " platform to your project first."); if (! hasSdk) { Console.info("First install the SDK by running: " + - Console.bold("meteor install-sdk " + platform)); + Console.command("meteor install-sdk " + platform)); Console.info("Then run: " + - Console.bold("meteor add-platform " + platform)); + Console.command("meteor add-platform " + platform)); } else { - Console.info("Run: " + Console.bold("meteor add-platform " + platform)); + Console.info("Run: " + Console.command("meteor add-platform " + platform)); } } throw new main.ExitWithCode(2); @@ -115,9 +115,9 @@ cordova.buildTargets = function (projectContext, targets, options) { if (supported) Console.warn("The " + displayPlatform + " platform is not installed;" + " please run: " + - Console.bold("meteor install-sdk " + platform)); + Console.command("meteor install-sdk " + platform)); else - Console.warn(Console.fail(MESSAGE_IOS_ONLY_ON_MAC)); + Console.failWarn(MESSAGE_IOS_ONLY_ON_MAC); throw new main.ExitWithCode(2); } @@ -184,7 +184,7 @@ var setVerboseness = cordova.setVerboseness = function (v) { }; var verboseLog = cordova.verboseLog = function (/* args */) { if (verboseness) - Console.stderr.write('%% ' + util.format.apply(null, arguments) + '\n'); + Console.rawError('%% ' + util.format.apply(null, arguments)); }; @@ -374,8 +374,8 @@ var ensureCordovaProject = function (projectContext, appName) { if (err instanceof main.ExitWithCode) { process.exit(err.code); } - Console.stderr.write("Error creating Cordova project: " + - err.message + "\n" + err.stack + "\n"); + Console.rawError("Error creating Cordova project: " + + err.message + "\n" + err.stack); } } }; @@ -904,14 +904,14 @@ var CordovaRunner = function (projectContext, platformName, options) { // projectContext being asynchronously reset.) if (self.platformName !== "ios" && self.projectContext.packageMap.getInfo('oauth2')) { - Console.warn( -"\n" + -"WARNING: It looks like you are using OAuth2 login in your app.\n" + -" Meteor's OAuth2 implementation does not currently work with\n" + -" mobile apps in local development mode, except in the iOS\n" + -" simulator. You can run the iOS simulator with 'meteor run ios'.\n" + -" For additional workarounds, see\n" + -" https://github.com/meteor/meteor/wiki/OAuth-for-mobile-Meteor-clients.\n"); + Console.warn(); + Console.labelWarn( + "It looks like you are using OAuth2 login in your app. " + + "Meteor's OAuth2 implementation does not currently work with " + + "mobile apps in local development mode, except in the iOS " + + "simulator. You can run the iOS simulator with 'meteor run ios'. " + + "For additional workarounds, see " + + "https://github.com/meteor/meteor/wiki/OAuth-for-mobile-Meteor-clients."); } }; @@ -1030,30 +1030,29 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { try { execFileSyncOrThrow('sh', args); } catch (err) { - Console.stderr.write([ - "", - chalk.green("Could not open your project in Xcode."), - chalk.green("Try running again with the --verbose option."), - chalk.green("Instructions for running your app on an iOS device:"), - chalk.cyan("https://github.com/meteor/meteor/wiki/How-to-run-your-app-on-an-iOS-device"), - "" - ].join("\n")); - + Console.error(); + Console.error( + chalk.green("Could not open your project in Xcode.") + + chalk.green("Try running again with the --verbose option.") + + chalk.green("Instructions for running your app on an iOS device:")+ + chalk.cyan( + "https://github.com/meteor/meteor/wiki/" + + "How-to-run-your-app-on-an-iOS-device") + ); + Console.error(); process.exit(2); } - - Console.stdout.write([ - "", - chalk.green([ - "Your project has been opened in Xcode so that you can run your app on ", - "an iOS device. For further instructions, visit this wiki page:", - ].join("\n")), + Console.info(); + Console.info( + chalk.green( + "Your project has been opened in Xcode so that you can run your " + + "app on an iOS device. For further instructions, visit this " + + "wiki page:") + chalk.cyan( - "https://github.com/meteor/meteor/wiki/How-to-run-your-app-on-an-iOS-device" - ), - "" - ].join("\n")); - + "https://github.com/meteor/meteor/wiki/" + + "How-to-run-your-app-on-an-iOS-device" + )); + Consoke.info(); } else { verboseLog('Running emulator:', localCordova, args); var emulatorOptions = { verbose: options.verbose, cwd: cordovaPath }; @@ -1068,35 +1067,38 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { localCordova, args, emulatorOptions, function(err, code) { if (err && platform === "android" && isDevice) { - Console.stderr.write([ - "", - chalk.green("Could not start the app on your device. Is it plugged in?"), - chalk.green("Try running again with the --verbose option."), - chalk.green("Instructions for running your app on an Android device:"), - chalk.cyan("https://github.com/meteor/meteor/wiki/How-to-run-your-app-on-an-Android-device"), - "" - ].join("\n")); + Console.error(); + Console.error( + chalk.green( + "Could not start the app on your device. Is it plugged in? " + + "Try running again with the --verbose option. " + + "Instructions for running your app on an Android device: ") + + chalk.cyan( + "https://github.com/meteor/meteor/wiki/" + + "How-to-run-your-app-on-an-Android-device") + ); + Console.error(); } else if (err && platform === "android") { - Console.stderr.write([ - "", + Console.error(); + Console.error( chalk.green("Could not start the app in the Android emulator."), - chalk.green("Try running again with the --verbose option."), - "" - ].join("\n")); + chalk.green("Try running again with the --verbose option.") + ); + Console.error(); } else if (err && platform === "ios") { - Console.stderr.write([ - "", + Console.error(); + Console.error( chalk.green("Could not start the app in the iOS simulator."), - chalk.green("Try running again with the --verbose option."), - "" - ].join("\n")); + chalk.green("Try running again with the --verbose option.") + ); + Console.error(); } else if (err) { - Console.stderr.write([ - "", + Console.error(); + Console.stderr.write( chalk.green("Could not start your app."), - chalk.green("Try running again with the --verbose option."), - "" - ].join("\n")); + chalk.green("Try running again with the --verbose option.") + ); + Console.error(); } // Don't throw an error or print the stack trace, but still exit the @@ -1282,7 +1284,7 @@ var checkAgreePlatformTerms = function (platform, name) { verboseLog("Error while downloading license terms: " + e); // most likely we don't have a net connection - Console.warn("Unable to download license terms for " + name + ".\n" + + Console.warn("Unable to download license terms for " + name + ". " + "Please make sure you are online.\n"); throw new main.ExitWithCode(2); } @@ -1292,9 +1294,12 @@ var checkAgreePlatformTerms = function (platform, name) { return true; } - Console.stdout.write("The following terms apply to " + name + ":\n\n"); - Console.stdout.write(terms + "\n\n"); - Console.stdout.write("You must agree to the terms to proceed.\n"); + Console.info("The following terms apply to " + name + ":"); + Console.info(); + Console.info(terms); + Console.info(); + Console.info("You must agree to the terms to proceed."); + Console.info(); var agreed = false; @@ -1312,7 +1317,8 @@ var checkAgreePlatformTerms = function (platform, name) { }; var checkPlatformRequirements = function (platform, options) { - options = _.extend({log: false, fix: false, fixConsole: false, fixSilent: false}, options); + options = _.extend( + { log: false, fix: false, fixConsole: false, fixSilent: false }, options); if (platform == 'android') { return Android.checkRequirements(options); } else if (platform == 'ios') { @@ -1327,7 +1333,9 @@ var requirePlatformReady = function (platform) { try { var installed = checkPlatformRequirements(platform); if (!installed.acceptable) { - Console.warn("The " + platformToHuman(platform) + " platform is not installed; please run: " + Console.bold("meteor install-sdk " + platform)); + Console.warn( + "The " + platformToHuman(platform) + " platform is not installed;", + "please run: " + Console.command("meteor install-sdk " + platform)); throw new main.ExitWithCode(2); } } catch (err) { @@ -1336,7 +1344,8 @@ var requirePlatformReady = function (platform) { } else if (err instanceof main.ExitWithCode) { throw err; } else { - Console.warn("Unexpected error while checking platform requirements: ", err); + Console.warn( + "Unexpected error while checking platform requirements: ", err); } throw new main.ExitWithCode(2); } @@ -1787,7 +1796,9 @@ _.extend(IOS.prototype, { } buildmessage.enterJob({title: 'Installing Xcode'}, function () { - //Console.info("Launching Xcode installer; please choose 'Get Xcode' to install Xcode"); + //Console.info( + // "Launching Xcode installer;", + // "please choose 'Get Xcode' to install Xcode"); //files.run('/usr/bin/xcodebuild', '--install'); // XXX: Any way to open direct in AppStore (rather than in browser)? @@ -1832,7 +1843,9 @@ _.extend(IOS.prototype, { var fix = !!options.fix; if (!Host.isMac()) { - log && Console.info("You are not running on OSX; we won't be able to install Xcode for local iOS development"); + log && Console.info( + "You are not running on OSX;", + "we won't be able to install Xcode for local iOS development"); return { acceptable: false, missing: [ "ios" ] }; } @@ -1840,14 +1853,14 @@ _.extend(IOS.prototype, { var okay = true; if (self.hasXcode()) { - log && Console.info(Console.success("Xcode is installed")); + log && Console.success("Xcode is installed"); } else { if (fix) { log && Console.info("Installing Xcode"); self.installXcode(); } else { - log && Console.info(Console.fail("Xcode is not installed")); + log && Console.failInfo("Xcode is not installed"); result.missing.push("xcode"); result.acceptable = false; @@ -1864,7 +1877,7 @@ _.extend(IOS.prototype, { if (self.hasXcode()) { if (self.hasAgreedXcodeLicense()) { - log && Console.info(Console.success("Xcode license agreed")); + log && Console.success("Xcode license agreed"); } else { if (fix) { log && Console.info("Please accept the Xcode license"); @@ -1873,7 +1886,7 @@ _.extend(IOS.prototype, { // XXX: Wait? } else { - log && Console.info(Console.fail("You must accept the Xcode license")); + log && Console.failInfo("You must accept the Xcode license"); result.missing.push("xcode-license"); result.acceptable = false; @@ -1882,14 +1895,15 @@ _.extend(IOS.prototype, { } _.each(['5.0', '5.0.1', '5.1', '6.0', '6.1'], function (version) { - if (self.isSdkInstalled(version)) { - log && Console.warn("An old version of the iPhone SDK is installed (" + version + "); you should"); - log && Console.warn("probably delete it. With SDK versions prior to 7.0 installed, your apps"); - log && Console.warn("can't be published to the App Store. Moreover, some Cordova plugins are"); - log && Console.warn("incompatible with this SDK."); - log && Console.info("You can remove it by deleting this directory: "); - log && Console.info(" " + self.getDirectoryForSdk(version)); - + if (self.isSdkInstalled(version) && log) { + Console.warn( + "An old version of the iPhone SDK is installed (" + version + ");", + "you should probably delete it. With SDK versions prior to 7.0", + "installed, your apps can't be published to the App Store.", + "Moreover, some Cordova plugins are incompatible with this SDK.", + "You can remove it by deleting this directory: "); + Console.warn( + self.getDirectoryForSdk(version), Console.options({ indent: 4 })); // Not really a failure; just warn... } }); @@ -1935,7 +1949,8 @@ _.extend(Android.prototype, { return stat != null; } - Console.info("Can't determine acceleration for unknown host: ", archinfo.host()); + Console.info( + "Can't determine acceleration for unknown host: ", archinfo.host()); return undefined; }, @@ -1956,7 +1971,9 @@ _.extend(Android.prototype, { files.mkdir_p(dir); fs.writeFileSync(filepath, mpkg); - Console.info("Launching HAXM installer; we recommend allocating 1024MB of RAM (or more)"); + Console.info( + "Launching HAXM installer;", + "we recommend allocating 1024MB of RAM (or more)"); files.run('open', filepath); return; @@ -1969,7 +1986,8 @@ _.extend(Android.prototype, { return; } - throw new Error("Can't install acceleration for unknown host: " + archinfo.host()); + throw new Error( + "Can't install acceleration for unknown host: " + archinfo.host()); }, useGlobalAdk: function () { @@ -1998,7 +2016,8 @@ _.extend(Android.prototype, { } if (!optional && !androidSdkPath) { - throw new Error("Cannot find Android SDK; be sure the 'android' tool is on your path"); + throw new Error( + "Cannot find Android SDK; be sure the 'android' tool is on your path"); } Console.debug("Using (global) Android SDK at", androidSdkPath); @@ -2054,9 +2073,10 @@ _.extend(Android.prototype, { } if (execution.exitCode !== 0) { - Console.warn("Unexpected exit code from android process: " + execution.exitCode); - Console.warn("stdout: " + execution.stdout); - Console.warn("stderr: " + execution.stderr); + Console.warn( + "Unexpected exit code from android process: " + execution.exitCode); + Console.rawWarn("stdout: " + execution.stdout); + Console.rawWarn("stderr: " + execution.stderr); throw new Error("Error running android tool: exit code " + execution.exitCode); } @@ -2175,9 +2195,9 @@ _.extend(Android.prototype, { if (execution.exitCode !== 0) { Console.debug("Unable to run aapt." + " (This is normal if 32 bit libraries are not found)"); - Console.debug(" exit code: " + execution.exitCode); - Console.debug(" stdout: " + execution.stdout); - Console.debug(" stderr: " + execution.stderr); + Console.rawDebug(" exit code: " + execution.exitCode); + Console.rawDebug(" stdout: " + execution.stdout); + Console.rawDebug(" stderr: " + execution.stderr); return false; } @@ -2376,25 +2396,36 @@ _.extend(Android.prototype, { if (Host.hasAptGet()) { Console.info("You can install the JDK using:"); - Console.info(" sudo apt-get install --yes openjdk-7-jdk"); + Console.rawInfo(" sudo apt-get install --yes openjdk-7-jdk"); // XXX: Technically, these are for Android, not installing Java if (processor == "x86_64") { Console.info("You will also need some 32-bit libraries:"); - Console.info(" sudo apt-get install --yes lib32z1 lib32stdc++6"); + Console.info( + Console.command("sudo apt-get install --yes lib32z1 lib32stdc++6"), + Console.options({ indent: 2 })); } } else if (Host.hasYum()) { Console.info("You can install the JDK using:"); - Console.info(" sudo yum install -y java-1.7.0-openjdk-devel"); + Console.info( + Console.command("sudo yum install -y java-1.7.0-openjdk-devel"), + Console.options({ indent: 2 })); // XXX: Technically, these are for Android, not installing Java if (processor == "x86_64") { Console.info("You will also need some 32-bit libraries:"); - Console.info(" sudo yum install -y glibc.i686 zlib.i686 libstdc++.i686 ncurses-libs.i686"); + Console.info( + Console.command( + "sudo yum install -y glibc.i686 zlib.i686 " + + "libstdc++.i686 ncurses-libs.i686"), + Console.options({ indent: 2 })); } } else { - Console.warn("You should install the JDK; we don't have instructions for your distribution (sorry!)"); - Console.info("Please do submit the instructions so we can include them.") + Console.warn( + "You should install the JDK; we don't have instructions", + "for your distribution (sorry!)"); + Console.info( + "Please do submit the instructions so we can include them."); } return; @@ -2463,9 +2494,10 @@ _.extend(Android.prototype, { } if (execution.exitCode != 0) { - Console.warn("Unexpected exit code from script: " + execution.exitCode); - Console.warn("stdout: " + execution.stdout); - Console.warn("stderr: " + execution.stderr); + Console.warn( + "Unexpected exit code from script: " + execution.exitCode); + Console.rawWarn("stdout: " + execution.stdout); + Console.rawWarn("stderr: " + execution.stderr); throw new Error('Could not download Android bundle'); } }); @@ -2484,8 +2516,12 @@ _.extend(Android.prototype, { // Boost timeout if not running HAXM/KVM if (self.hasAcceleration() === false) { - Console.warn("Android emulator acceleration was not installed; the emulator will be very slow."); - Console.info("You can run '" + Console.command("meteor install-sdk android") + "' for help.") + Console.warn( + "Android emulator acceleration was not installed;", + "the emulator will be very slow."); + Console.info( + "You can run '" + + Console.command("meteor install-sdk android") + "' for help."); timeLimit *= 4; } @@ -2503,11 +2539,15 @@ _.extend(Android.prototype, { Console.error("The emulator did not start in the expected time."); if (self.hasAcceleration() === false) { if (Host.isLinux()) { - Console.info("We highly recommend enabling KVM to speed up the emulator."); + Console.info( + "We highly recommend enabling KVM to speed up the emulator."); } else { - Console.info("We highly recommend installing HAXM to speed up the emulator."); + Console.info( + "We highly recommend installing HAXM to speed up the emulator."); } - Console.info("You can run '" + Console.command("meteor install-sdk android") + "' for help.") + Console.info( + "You can run '" + + Console.command("meteor install-sdk android") + "' for help."); } throw new main.ExitWithCode(1); @@ -2552,7 +2592,7 @@ _.extend(Android.prototype, { device[kv[0]] = kv[1]; } devices.push(device); - Console.debug("Found device", JSON.stringify(device)); + Console.rawDebug("Found device", JSON.stringify(device)); }); return devices; }, @@ -2581,7 +2621,7 @@ _.extend(Android.prototype, { var hasAndroid = false; if (!self.useGlobalAdk()) { if (self.hasAndroidBundle()) { - log && Console.info(Console.success("Found Android bundle")); + log && Console.success("Found Android bundle"); hasAndroid = true; } else { if (fixConsole) { @@ -2590,7 +2630,7 @@ _.extend(Android.prototype, { self.installAndroidBundle(); hasAndroid = true; } else { - log && Console.info(Console.fail("Android bundle not found")); + log && Console.failInfo("Android bundle not found"); result.missing.push("android-bundle"); result.acceptable = false; @@ -2601,14 +2641,14 @@ _.extend(Android.prototype, { if (self.useGlobalAdk()) { var androidSdk = self.findAndroidSdk(true); if (androidSdk) { - log && Console.info(Console.success("Found Android SDK")); + log && Console.success("Found Android SDK"); // XXX: Verify hasAndroid = true; } else { - log && Console.info(Console.fail("Android SDK not found")); - - log && Console.info("If you set USE_GLOBAL_ADK, the 'android' tool must be on your path"); + log && Console.failInfo("Android SDK not found"); + log && Console.info( + "If you set USE_GLOBAL_ADK, the 'android' tool must be on your path"); result.missing.push("android-global-sdk"); result.acceptable = false; @@ -2616,9 +2656,9 @@ _.extend(Android.prototype, { var hasAnt = !!Host.which("ant"); if (hasAnt) { - log && Console.info(Console.success("Found ant on PATH")); + log && Console.success("Found ant on PATH"); } else { - log && Console.info(Console.fail("Ant not found on PATH")); + log && Console.failInfo("Ant not found on PATH"); result.missing.push("apache-ant"); result.acceptable = false; @@ -2627,7 +2667,7 @@ _.extend(Android.prototype, { var hasJava = false; if (self.hasJdk()) { - log && Console.info(Console.success("A JDK is installed")); + log && Console.success("A JDK is installed"); hasJava = true; } else { if (fix) { @@ -2636,7 +2676,7 @@ _.extend(Android.prototype, { self.installJdk(); hasJava = true; } else { - log && Console.info(Console.fail("A JDK is not installed")); + log && Console.failInfo("A JDK is not installed"); result.missing.push("jdk"); result.acceptable = false; @@ -2645,16 +2685,16 @@ _.extend(Android.prototype, { if (hasAndroid && hasJava) { if (self.isPlatformToolsInstalled()) { - log && Console.info(Console.success("Found Android Platform tools")); + log && Console.success("Found Android Platform tools"); } else { if (fixSilent) { log && Console.info("Installing Android Platform tools"); self.installTarget('platform-tools', function () { return self.isPlatformToolsInstalled(); }); - log && Console.info(Console.success("Installed Android Platform tools")); + log && Console.success("Installed Android Platform tools"); } else { - log && Console.info(Console.fail("Android Platform tools not found")); + log && Console.failInfo("Android Platform tools not found"); result.missing.push("android-platform-tools"); result.acceptable = false; @@ -2663,7 +2703,7 @@ _.extend(Android.prototype, { var hasBuildToolsVersion; if (self.isBuildToolsInstalled('21.0.0')) { - log && Console.info(Console.success("Found Android Build Tools")); + log && Console.success("Found Android Build Tools"); hasBuildToolsVersion = '21.0.0'; } else { if (fixSilent) { @@ -2671,10 +2711,10 @@ _.extend(Android.prototype, { self.installTarget('build-tools-21.0.0', function () { return self.isBuildToolsInstalled('21.0.0'); }); - log && Console.info(Console.success("Installed Android Build Tools")); + log && Console.success("Installed Android Build Tools"); hasBuildToolsVersion = '21.0.0'; } else { - log && Console.info(Console.fail("Android Build Tools not found")); + log && Console.failInfo("Android Build Tools not found"); result.missing.push("android-build-tools"); result.acceptable = false; @@ -2685,7 +2725,7 @@ _.extend(Android.prototype, { // Check that we can actually run aapt - on 64 bit, we need 32 bit libs // We need aapt to be installed to do this! if (!self.canRunAapt(hasBuildToolsVersion)) { - log && Console.info(Console.fail("32-bit libraries not found")); + log && Console.failInfo("32-bit libraries not found"); result.missing.push("libs32"); result.acceptable = false; @@ -2693,31 +2733,33 @@ _.extend(Android.prototype, { } if (self.isPlatformInstalled('android-19')) { - log && Console.info(Console.success("Found Android 19 API")); + log && Console.success("Found Android 19 API"); } else { if (fixSilent) { log && Console.info("Installing Android 19 API"); self.installTarget('android-19', function () { return self.isPlatformInstalled('android-19'); }); - log && Console.info(Console.success("Installed Android 19 API")); + log && Console.success("Installed Android 19 API"); } else { - log && Console.info(Console.fail("Android API 19 not found")); + log && Console.failInfo("Android API 19 not found"); result.missing.push("android-api"); result.acceptable = false; } } - // (We could alternatively check for {SDK}/system-images/android-19/default/x86/build.prop) + // (We could alternatively check for + // {SDK}/system-images/android-19/default/x86/build.prop) if (self.hasTarget('19', 'default/x86')) { - log && Console.info(Console.success("Found suitable Android x86 image")); + log && Console.success("Found suitable Android x86 image"); } else { if (fixSilent) { // The x86 image will fail to install if dependencies aren't there; // we've checked the others by version,but we should double-check // platform-tools as we don't check versions there - log && Console.info("Making sure Android Platform tools are up to date"); + log && Console.info( + "Making sure Android Platform tools are up to date"); self.installTarget('platform-tools', function () { return self.isPlatformToolsInstalled(); }); @@ -2726,9 +2768,9 @@ _.extend(Android.prototype, { self.installTarget('sys-img-x86-android-19', function () { return self.hasTarget('19', 'default/x86'); }); - log && Console.info(Console.success("Installed Android x86 image")); + log && Console.success("Installed Android x86 image"); } else { - log && Console.info(Console.fail("Suitable Android x86 image not found")); + log && Console.failInfo("Suitable Android x86 image not found"); result.missing.push("android-sys-img"); result.acceptable = false; @@ -2737,21 +2779,26 @@ _.extend(Android.prototype, { var avdName = self.getAvdName(); if (self.hasAvd(avdName)) { - log && Console.info(Console.success("'" + avdName + "' android virtual device (AVD) found")); + log && Console.success( + "'" + avdName + "' android virtual device (AVD) found"); } else { var isDefaultAvd = avdName === DEFAULT_AVD_NAME; if (fixSilent && isDefaultAvd) { - log && Console.info("Creating android virtual device (AVD): " + avdName); - + log && Console.info( + "Creating android virtual device (AVD): " + avdName); var avdOptions = {}; self.createAvd(avdName, avdOptions); - log && Console.info(Console.success("'" + avdName + "' android virtual device (AVD) created")); + log && Console.success( + "'" + avdName + "' android virtual device (AVD) created"); } else { - log && Console.info(Console.fail("'" + avdName + "' android virtual device (AVD) not found")); + log && Console.failInfo( + "'" + avdName + "' android virtual device (AVD) not found"); if (!isDefaultAvd) { - log && Console.info("(Because you specified a custom AVD, we don't create it automatically)"); + log && Console.info( + "(Because you specified a custom AVD, we don't create it", + "automatically)"); } result.missing.push("android-avd"); @@ -2766,18 +2813,21 @@ _.extend(Android.prototype, { if (fix) { self.installAcceleration(); } else { - log && Console.info(Console.fail("Android emulator acceleration is not installed")); - log && Console.info(" (The Android emulator will be very slow without acceleration)"); + log && Console.failInfo( + "Android emulator acceleration is not installed"); + log && Console.info( + "(The Android emulator will be very slow without acceleration)", + Console.options({ indent: 2 })); result.missing.push("haxm"); // Not all systems can install the accelerator, so don't block - // XXX: Maybe we should block the emulator (only); it is unusable without it - //result.acceptable = false + // XXX: Maybe we should block the emulator (only); it is unusable + //without it result.acceptable = false } } else if (hasAcceleration === true) { // (can be undefined) - log && Console.info(Console.success("Android emulator acceleration is installed")); + log && Console.success("Android emulator acceleration is installed"); } return result; @@ -2886,19 +2936,19 @@ main.registerCommand({ // explain why we can't remove server or browser platforms if (_.contains(projectContextModule.PlatformList.DEFAULT_PLATFORMS, platform)) { - Console.stdout.write(platform + ": cannot remove platform " + + Console.info(platform + ": cannot remove platform " + "in this version of Meteor\n"); return; } if (_.contains(platforms, platform)) { - Console.stdout.write(platform + ": removed platform\n"); + Console.info(platform + ": removed platform"); platforms = _.without(platforms, platform); changed = true; return; } - Console.stdout.write(platform + ": platform is not in this project\n"); + Console.info(platform + ": platform is not in this project."); }); if (! changed) { @@ -2927,7 +2977,7 @@ main.registerCommand({ var platforms = projectContext.platformList.getPlatforms(); - Console.stdout.write(platforms.join("\n")); + Console.rawInfo(platforms.join("\n")); }); main.registerCommand({ @@ -2978,7 +3028,9 @@ main.registerCommand({ if (!Android.hasAvd(avd)) { Console.error("'" + avd + "' android virtual device (AVD) does not exist"); - Console.info("The default AVD is called meteor, and will be created automatically for you"); + Console.info( + "The default AVD is called meteor, and will be created", + "automatically for you"); return 1; } @@ -3023,7 +3075,7 @@ main.registerCommand({ var installed = checkPlatformRequirements(platform, { log:true, fix: false, fixConsole: true, fixSilent: true } ); if (!_.isEmpty(installed.missing)) { if (Host.isLinux() && platform === "ios") { - Console.warn(Console.fail(MESSAGE_IOS_ONLY_ON_MAC)); + Console.failWarn(MESSAGE_IOS_ONLY_ON_MAC); return 1; } @@ -3043,7 +3095,8 @@ main.registerCommand({ url += "#" + anchor; } openUrl(url); - Console.info("Please follow the instructions here:\n" + Console.bold(url) + "\n"); + Console.info( + "Please follow the instructions here:\n" + Console.bold(url) + "\n"); } else { Console.info("We don't have installation instructions for your platform"); } diff --git a/tools/commands-packages.js b/tools/commands-packages.js index b145120d58..8bd197a71a 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -27,7 +27,8 @@ var Console = require('./console.js').Console; var projectContextModule = require('./project-context.js'); var packageVersionParser = require('./package-version-parser.js'); -// On some informational actions, we only refresh the package catalog if it is > 15 minutes old +// On some informational actions, we only refresh the package catalog if it is > +// 15 minutes old var DEFAULT_MAX_AGE_MS = 15 * 60 * 1000; // Returns an object with keys: @@ -58,7 +59,7 @@ var refreshOfficialCatalogOrDie = function (options) { var explainIfRefreshFailed = function () { if (catalog.official.offline || catalog.refreshFailed) { - Console.info("Your package catalog may be out of date.\n" + + Console.info("Your package catalog may be out of date. " + "Please connect to the internet and try again."); } }; @@ -153,7 +154,6 @@ main.registerCommand({ // not actually in the app! // XXX Maybe we should do a first pass that only builds packages actually in // the app and does display the PackageMapDelta? - return 0; }); @@ -260,9 +260,9 @@ main.registerCommand({ // weird. Let's not allow this. Console.error( "The package you are in appears to be inside a Meteor app but is not " + - "in its packages directory. You may only publish packages that are " + - "entirely outside of a project or that are loaded by the project " + - "that they are inside."); + "in its packages directory. You may only publish packages that are " + + "entirely outside of a project or that are loaded by the project " + + "that they are inside."); return 1; } var packageName = localVersionRecord.packageName; @@ -290,10 +290,10 @@ main.registerCommand({ if (!options['top-level'] && !packageName.match(/:/)) { Console.error( -"Only administrators can create top-level packages without an account prefix.\n" + -"(To confirm that you wish to create a top-level package with no account\n" + -"prefix, please run this command again with the --top-level option.)"); - + "Only administrators can create top-level packages without an", + "account prefix. (To confirm that you wish to create a top-level", + "package with no account prefix, please run this command again", + "with the --top-level option.)"); // You actually shouldn't be able to get here without being logged in, but // it seems poor form to assume anything like that for the point of a // brief error message. @@ -360,28 +360,41 @@ main.registerCommand({ // This is an undocumented command that you are not supposed to run! We // assume that you know what you are doing, if you ran it, and are OK with // overrwriting normal compatibilities. - Console.warn("\nWARNING: Your package contains binary code."); + Console.warn(); + Console.labelWarn("Your package contains binary code."); } else if (binary) { // Normal publish flow. Tell the user nicely. + Console.warn(); Console.warn( -"\nThis package contains binary code and must be built on multiple architectures.\n"); - + "This package contains binary code and must be built on", + "multiple architectures."); + Console.warn(); Console.info( -"You can access Meteor provided build machines, pre-configured to support\n" + -"older versions of MacOS and Linux, by running:\n"); - - _.each(["os.osx.x86_64", "os.linux.x86_64", "os.linux.x86_32"], function (a) { - Console.info(" meteor admin get-machine", a); + "You can access Meteor provided build machines, pre-configured to", + "support older versions of MacOS and Linux, by running:"); + _.each(["os.osx.x86_64", "os.linux.x86_64", "os.linux.x86_32"], + function (a) { + Console.info( + Console.command("meteor admin get-machine " + a), + Console.options({ indent: 2 })); }); - Console.info("\nOn each machine, run:\n"); - - Console.info(" meteor", - "publish-for-arch", - packageSource.name + "@" + packageSource.version); - - Console.info("\nFor more information on binary ABIs and consistent builds, see:"); - Console.info(" https://github.com/meteor/meteor/wiki/Build-Machines\n"); + Console.info(); + Console.info("On each machine, run:"); + Console.info(); + Console.info( + Console.command( + "meteor publish-for-arch " + + packageSource.name + "@" + packageSource.version), + Console.options({ indent: 2 })); + Console.info(); + Console.info( + "For more information on binary ABIs and consistent builds, see:"); + Console.info( + "https://github.com/meteor/meteor/wiki/Build-Machines", + Console.options({ indent: 2 }) + ); + Console.info(); } // Refresh, so that we actually learn about the thing we just published. @@ -411,26 +424,36 @@ main.registerCommand({ var packageInfo = catalog.official.getPackage(name); if (! packageInfo) { Console.error( -"You can't call `meteor publish-for-arch` on package '" + name + "' without\n" + -"publishing it first.\n\n" + -"To publish the package, run `meteor publish --create` from the package directory.\n"); - + "You can't call " + Console.command("`meteor publish-for-arch`") + + "on package '" + name + "' without " +" publishing it first." + ); + Console.error(); + Console.error( + "To publish the package, run " + + Console.command("`meteor publish --create` ") + + "from the package directory."); + Console.error(); return 1; } var pkgVersion = catalog.official.getVersion(name, versionString); if (! pkgVersion) { Console.error( -"You can't call `meteor publish-for-arch` on version " + versionString + " of\n" + -"package '" + name + "' without publishing it first.\n\n" + -"To publish the version, run `meteor publish` from the package directory.\n\n"); - + "You can't call", Console.command("`meteor publish-for-arch`"), + "on version " + versionString + " of " + "package '" + name + + "' without publishing it first."); + Console.error(); + Console.error( + "To publish the package, run " + Console.command("`meteor publish ` ") + + "from the package directory."); + Console.error(); return 1; } if (! pkgVersion.source || ! pkgVersion.source.url) { - Console.error('There is no source uploaded for ' + - name + '@' + versionString); + Console.error( + "There is no source uploaded for", + name + '@' + versionString); return 1; } @@ -439,30 +462,33 @@ main.registerCommand({ // further springboarding based on reading a nested json file. if (! _.has(pkgVersion, 'releaseName')) { if (files.inCheckout()) { - process.stderr.write( - "This package was published from an old version of meteor," + - "but you are running from checkout!\nConsider running " + - "`meteor --release 1.0`, so we can springboard correctly.\n"); - process.stderr.exit(1); + Console.error( + "This package was published from an old version of meteor, " + + "but you are running from checkout! Consider running " + + Console.command("`meteor --release 1.0`"), + "so we can springboard correctly."); + process.exit(1); } throw new main.SpringboardToSpecificRelease("METEOR@1.0"); } if (pkgVersion.releaseName === null) { if (! files.inCheckout()) { - process.stderr.write( - "This package was published from a checkout of meteor! The tool cannot replicate\n" + - "that environment and will not even try. Please check out meteor at the \n" + - "corresponding git commit and try again.\n"); + Console.error( + "This package was published from a checkout of meteor!", + "The tool cannot replicate that environment and will not even try.", + "Please check out meteor at the " + + "corresponding git commit and try again."); process.exit(1); } } else if (files.inCheckout()) { - process.stderr.write( - "This package was published from a built version of meteor," + - "but you are running from checkout!\nConsider running from a " + - "proper Meteor release with `meteor --release " + - pkgVersion.releaseName + "` so we can springboard correctly.\n"); - process.stderr.exit(1); + Console.error( + "This package was published from a built version of meteor, " + + "but you are running from checkout! Consider running from a " + + "proper Meteor release with " + + Console.command("`meteor --release " + pkgVersion.releaseName + "`"), + "so we can springboard correctly."); + process.exit(1); } else if (pkgVersion.releaseName !== release.current.name) { // We are in a built release, and so is the package, but it's a different // one. Springboard! @@ -496,10 +522,11 @@ main.registerCommand({ // Copy over a version lock file from the source tarball. var versionsFile = path.join(packageDir, '.versions'); if (! fs.existsSync(versionsFile)) { - process.stderr.write( - "This package has no valid version lock file: are you trying to use publish-for-arch on\n" + - "a core package? Publish-for-arch cannot guarantee safety. Please use\n" + - "publish --existing-version instead.\n"); + Console.error( + "This package has no valid version lock file: are you trying to use " + + "publish-for-arch on a core package? Publish-for-arch cannot " + + "guarantee safety. Please use", + Console.command("'meteor publish --existing-version'"), "instead."); process.exit(1); } files.copyFile(path.join(packageDir, '.versions'), @@ -640,9 +667,9 @@ main.registerCommand({ if (start === "0.8." || start === "0.7." || start === "0.6." || start === "0.5.") { buildmessage.error( - "It looks like you are trying to publish a pre-package-server meteor release.\n" + - "Doing this through the package server is going to cause a lot of confusion.\n" + - "Please use the old release process."); + "It looks like you are trying to publish a pre-package-server " + + "meteor release. Doing this through the package server is going " + + "to cause a lot of confusion. Please use the old release process."); } } } @@ -656,7 +683,7 @@ main.registerCommand({ if (!trackRecord) { Console.error( 'There is no release track named ' + relConf.track + - '. If you are creating a new track, use the --create-track flag.'); + '. If you are creating a new track, use the --create-track flag.'); return 1; } @@ -696,9 +723,9 @@ main.registerCommand({ // these by accident. So, we will disallow it for now. if (relConf.packages || relConf.tool) { Console.error( - "Setting the --from-checkout option will use the tool and packages in your meteor " + - "checkout.\n" + - "Your release configuration file should not contain that information."); + "Setting the --from-checkout option will use the tool and packages " + + "in your meteor checkout. " + + "Your release configuration file should not contain that information."); return 1; } @@ -924,13 +951,17 @@ main.registerCommand({ Console.error( "Failed to push git tag. Please push git tag manually!"); Console.error( - "If you are publishing a non-prerelease version, then the readme will show up " + - "in atmosphere. To make sure that happens, after pushing the git tag, please " + - "run the following:"); + "If you are publishing a non-prerelease version, then the readme " + + "should show up in Atmosphere. To make sure that happens, after " + + "pushing the git tag, please run the following:"); _.each(toPublish, function (name) { - Console.info("meteor admin set-latest-readme " + name + " --tag " + gitTag); + Console.info( + Console.command( + "meteor admin set-latest-readme " + name + " --tag " + gitTag)); }); - Console.error("If you are publishing an experimental version, don't worry about it."); + Console.error( + "If you are publishing an experimental version, ", + "don't worry about it."); fail = true; } if (! fail) { @@ -938,14 +969,15 @@ main.registerCommand({ var isopk = projectContext.isopackCache.getIsopack(name); if (! isopk) throw Error("no isopack for " + name); - - var url = "https://raw.githubusercontent.com/meteor/meteor/" + gitTag + + var url = + "https://raw.githubusercontent.com/meteor/meteor/" + gitTag + "/packages/" + name + "/README.md"; var version = isopk.version; packageClient.callPackageServer( conn, '_changeReadmeURL', name, version, url); - Console.info("Setting the readme of", name + "@" + version, "to", url); + Console.info( + "Setting the readme of", name + "@" + version, "to", url); }); } } @@ -954,12 +986,13 @@ main.registerCommand({ // packages. Unlike publish, this is advanced functionality, so the user // should be familiar with the concept. if (! _.isEmpty(unfinishedBuilds)) { - Console.warning(); - Console.warning( - "WARNING: Some packages contain binary dependencies."); - Console.warning("Builds have not been published for the following packages:"); + Console.warn(); + Console.labelWarn( + "Some packages contain binary dependencies."); + Console.warn( + "Builds have not been published for the following packages:"); _.each(unfinishedBuilds, function (version, name) { - Console.warning(name + "@" + version); + Console.warn(name + "@" + version); }); // Note: we don't actually enforce the proper build machine thing. You // can't use publish-for-arch for meteor-tool, for example, you need to @@ -969,7 +1002,7 @@ main.registerCommand({ // --existing-version: presumably you don't care about compatibility // etc. If it is an official release, you ought to use a build machine // though. - Console.warning( + Console.warn( "Please publish the builds separately, from a proper build machine."); } } @@ -1097,20 +1130,25 @@ main.registerCommand({ if (v.buildArchitectures) { var buildArchitectures = v.buildArchitectures.split(' '); - Console.info(" Architectures: ", formatAsList(buildArchitectures, { formatter: formatArchitecture })); + Console.info( + "Architectures: ", + formatAsList( + buildArchitectures, { formatter: formatArchitecture }), + Console.options({ indent: 6 })); } // XXX: else show "no architectures"? if (v.packages) { - Console.info(" tool: " + v.tool); - Console.info(" packages:"); + Console.info("tool: " + v.tool, Console.options({ indent: 6 })); + Console.info("packages:", Console.options({ indent: 6 })); _.each(v.packages, function(pv, pn) { - Console.info(" " + pn + "@" + pv); + Console.info(pn + "@" + pv, Console.options({ indent: 6 })); }); } }); - Console.info("\n"); + Console.info(); + Console.info(); } else { // Non-detailed list of versions @@ -1126,7 +1164,7 @@ main.registerCommand({ rows.push(row); }); - utils.printTwoColumns(rows); + Console.printTwoColumns(rows); } } @@ -1136,7 +1174,7 @@ main.registerCommand({ var myMaintainerString = ""; var myMaintainers = _.pluck(record.maintainers, 'username'); if (myMaintainers.length === 0) { - Console.debug("No maintainer records found: ", JSON.stringify(record)); + Console.rawDebug("No maintainer records found: ", JSON.stringify(record)); } else if (myMaintainers.length === 1) { myMaintainerString = myMaintainers[0]; } else { @@ -1180,7 +1218,8 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.OnceAtStart({ maxAge: DEFAULT_MAX_AGE_MS, ignoreErrors: true }) }, function (options) { if (options.args.length === 0) { - Console.info("To show all packages, do", Console.command("meteor search .")); + Console.info( + "To show all packages, do", Console.command("meteor search .")); return 1; } @@ -1310,7 +1349,8 @@ main.registerCommand({ explainIfRefreshFailed(); } else { Console.info( - "To get more information on a specific item, use", Console.command("meteor show")); + "To get more information on a specific item, use", + Console.command("meteor show")); } }); @@ -1405,21 +1445,26 @@ main.registerCommand({ utils.printPackageList(items); if (newVersionsAvailable) { - Console.info("\n" + -"* New versions of these packages are available! Run 'meteor update' to try\n" + -" to update those packages to their latest versions. If your packages cannot be\n" + -" updated further, try typing meteor add @ to see more\n" + -" information."); + Console.info(); + Console.info( + "New versions of these packages are available! Run", + Console.command("'meteor update'"), "to try to update those", + "packages to their latest versions. If your packages cannot be", + "updated further, try typing", + Console.command("`meteor add @`"), + "to see more information.", + Console.options({ bulletPoint: "* " })); } if (anyBuiltLocally) { - Console.info("\n" + -"+ These packages are built locally from source."); + Console.info(); + Console.info( + "These packages are built locally from source.", + Console.options({ bulletPoint: "+ " })); } return 0; }); - /////////////////////////////////////////////////////////////////////////////// // update /////////////////////////////////////////////////////////////////////////////// @@ -1437,8 +1482,8 @@ var maybeUpdateRelease = function (options) { // We are running from checkout, so we are not updating the release. if (release.current && release.current.isCheckout()) { Console.error( -"You are running Meteor from a checkout, so we cannot update the Meteor release.\n" + -"Checking to see if we can update your packages."); + "You are running Meteor from a checkout, so we cannot update", + "the Meteor release. Checking to see if we can update your packages."); return 0; } @@ -1508,18 +1553,18 @@ var maybeUpdateRelease = function (options) { // command even ran. They could equivalently have run 'meteor // help --release xyz'. Console.info( - "Installed. Run 'meteor update' inside of a particular project\n" + - "directory to update that project to " + - release.current.getDisplayName() + "."); + "Installed. Run " + Console.command("'meteor update' ") + + "inside of a particular project directory to update that project to " + + release.current.getDisplayName() + "."); } else { // We get here if the user ran 'meteor update' and we didn't // find a new version. Console.info( "The latest version of Meteor, " + release.current.getReleaseVersion() + - ", is already installed on this\n" + - "computer. Run 'meteor update' inside of a particular project\n" + - "directory to update that project to " + - release.current.getDisplayName()); + ", is already installed on this computer. Run " + + Console.command("'meteor update'") + " inside of a particular " + + "project directory to update that project to " + + release.current.getDisplayName()); } return 0; } @@ -1544,7 +1589,7 @@ var maybeUpdateRelease = function (options) { var maybeTheLatestRelease = release.explicit ? "" : ", the latest release"; Console.info( "This project is already at " + - release.current.getDisplayName() + maybeTheLatestRelease + "."); + release.current.getDisplayName() + maybeTheLatestRelease + "."); return 0; } @@ -1616,9 +1661,9 @@ var maybeUpdateRelease = function (options) { // We could not find any releases newer than the one that we are on, on // that track, so we are done. Console.info( -"This project is already at " + projectContext.releaseFile.displayReleaseName + -", which is newer\n" + -"than the latest release."); + "This project is already at " + + projectContext.releaseFile.displayReleaseName + + ", which is newer than the latest release."); return 0; } } @@ -1639,7 +1684,8 @@ var maybeUpdateRelease = function (options) { // Nope, this release didn't work. Console.debug( "Update to release", releaseTrack + "@" + versionToTry, - "is impossible:\n" + messages.formatMessages()); + "is impossible:"); + Console.debug(messages.formatMessages()); return false; } @@ -1651,8 +1697,8 @@ var maybeUpdateRelease = function (options) { var newerAvailable = false; if (! solutionReleaseVersion) { Console.info( - "This project is at the latest release which is compatible with your\n" + - "current package constraints."); + "This project is at the latest release which is compatible with your " + + "current package constraints."); return 0; } else if (solutionReleaseVersion !== releaseVersionsToTry[0]) { newerAvailable = true; @@ -1689,8 +1735,8 @@ var maybeUpdateRelease = function (options) { projectContext.releaseFile.displayReleaseName + "."); if (newerAvailable) { Console.info( - "(Newer releases are available but are not compatible with your\n" + - "current package constraints.)"); + "(Newer releases are available but are not compatible with your " + + "current package constraints.)"); } // Now run the upgraders. @@ -2001,7 +2047,7 @@ main.registerCommand({ }); }); if (messages.hasMessages()) { - Console.error("=> Errors while parsing arguments:"); + Console.arrowError("Errors while parsing arguments:", 1); Console.printMessages(messages); explainIfRefreshFailed(); // this is why we're not using captureAndExit return 1; @@ -2014,7 +2060,7 @@ main.registerCommand({ projectContext.prepareProjectForBuild(); }); if (messages.hasMessages()) { - Console.error("=> Errors while adding packages:"); + Console.arrowError("Errors while adding packages:", 1); Console.printMessages(messages); explainIfRefreshFailed(); // this is why we're not using captureAndExit return 1; @@ -2026,12 +2072,12 @@ main.registerCommand({ projectContext.packageMapDelta.displayOnConsole(); // Show descriptions of directly added packages. - Console.stdout.write("\n"); + Console.info(); _.each(constraintsToAdd, function (constraint) { var version = projectContext.packageMap.getInfo(constraint.name).version; var versionRecord = projectContext.projectCatalog.getVersion( constraint.name, version); - Console.stdout.write( + Console.info( constraint.name + (versionRecord.description ? (": " + versionRecord.description) : "")); }); @@ -2178,7 +2224,8 @@ main.registerCommand({ } if ((options.add || options.remove) && options.list) { Console.error( -"Sorry, you can't change the users at the same time as you're listing them."); + "Sorry, you can't change the users at the same time as you're", + "listing them."); return 1; } @@ -2213,7 +2260,7 @@ main.registerCommand({ packageClient.callPackageServer( conn, 'removeMaintainer', name, options.remove); } - Console.info(" Done!"); + Console.info("Success."); } } catch (err) { packageClient.handlePackageServerConnectionError(err); @@ -2230,11 +2277,13 @@ main.registerCommand({ if (!record) { Console.info( -"Could not get list of maintainers: package " + name + " does not exist."); + "Could not get list of maintainers:", + "package " + name + " does not exist."); return 1; } - Console.info("\nThe maintainers for " + name + " are:"); + Console.info(); + Console.info("The maintainers for " + name + " are:"); _.each(record.maintainers, function (user) { if (! user || !user.username) Console.info(""); @@ -2482,14 +2531,15 @@ main.registerCommand({ var name = release[0]; var version = release[1]; if (!version) { - Console.error('\n Must specify release version (track@version)'); + Console.error('Must specify release version (track@version)'); return 1; } // Now let's get down to business! Fetching the thing. var record = catalog.official.getReleaseTrack(name); if (!record) { - Console.error('\n There is no release track named ' + name); + Console.error(); + Console.error('There is no release track named ' + name); return 1; } @@ -2503,14 +2553,15 @@ main.registerCommand({ try { if (options.unrecommend) { Console.info("Unrecommending " + name + "@" + version + "..."); - packageClient.callPackageServer(conn, 'unrecommendVersion', name, version); - Console.info("Done!\n " + name + "@" + version + - " is no longer a recommended release"); + packageClient.callPackageServer( + conn, 'unrecommendVersion', name, version); + Console.info("Success."); + Console.info(name + "@" + version, "is no longer a recommended release"); } else { Console.info("Recommending " + options.args[0] + "..."); packageClient.callPackageServer(conn, 'recommendVersion', name, version); - Console.info("Done!\n " + name + "@" + version + - " is now a recommended release"); + Console.info("Success."); + Console.info(name + "@" + version, "is now a recommended release"); } } catch (err) { packageClient.handlePackageServerConnectionError(err); @@ -2538,7 +2589,8 @@ main.registerCommand({ // Now let's get down to business! Fetching the thing. var record = catalog.official.getPackage(name); if (!record) { - Console.error('\n There is no package named ' + name); + Console.error(); + Console.error('There is no package named ' + name); return 1; } @@ -2601,16 +2653,17 @@ main.registerCommand({ try { var status = options.success ? "successfully" : "unsuccessfully"; + // XXX: This should probably use progress bars instead. _.each(versions, function (version) { - process.stdout.write( - "Setting " + var migrating = "Setting " + name + "@" + version + " as " + - status + " migrated ..."); + status + " migrated ..."; + process.stdout.write(migrating + "\r"); packageClient.callPackageServer( conn, '_changeVersionMigrationStatus', name, version, !options.success); - process.stdout.write(" done!\n"); + Console.info(migrating + "done."); }); } catch (err) { packageClient.handlePackageServerConnectionError(err); @@ -2656,14 +2709,15 @@ main.registerCommand({ } try { - Console.info( - "Setting README of " - + name + "@" + version + " to " + url); - packageClient.callPackageServer( - conn, - '_changeReadmeURL', - name, version, url); - Console.info(" done!\n"); + // XXX: This output should probably use progress bars instead! + var setting = + "Setting README of " + name + "@" + version + " to " + url + " ..."; + process.stdout.write(setting + "\r"); + packageClient.callPackageServer( + conn, + '_changeReadmeURL', + name, version, url); + Console.info(setting + " done."); } catch (err) { packageClient.handlePackageServerConnectionError(err); return 1; diff --git a/tools/commands.js b/tools/commands.js index cec9c51349..7dc6b6626d 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -71,7 +71,9 @@ var showInvalidArchMsg = function (arch) { Console.info("Invalid architecture: " + arch); Console.info("The following are valid Meteor architectures:"); _.each(_.keys(VALID_ARCHITECTURES), function (va) { - Console.info(" " + va); + Console.info( + Console.command(va), + Console.options({ indent: 2 })); }); }; @@ -86,7 +88,7 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { var archinfo = require('./archinfo.js'); - console.log(archinfo.host()); + Console.info(archinfo.host()); }); // Prints the current release in use. Note that if there is not @@ -102,15 +104,16 @@ main.registerCommand({ if (release.current === null) { if (! options.appDir) throw new Error("missing release, but not in an app?"); - Console.stderr.write( -"This project was created with a checkout of Meteor, rather than an\n" + -"official release, and doesn't have a release number associated with\n" + -"it. You can set its release with 'meteor update'.\n"); + Console.error( + "This project was created with a checkout of Meteor, rather than an " + + "official release, and doesn't have a release number associated with " + + "it. You can set its release with " + + Console.command("'meteor update'") + "."); return 1; } if (release.current.isCheckout()) { - Console.stderr.write("Unreleased (running from a checkout)\n"); + Console.error("Unreleased (running from a checkout)."); return 1; } @@ -124,15 +127,15 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { if (files.inCheckout()) { - Console.stderr.write("checkout\n"); + Console.error("checkout"); return 1; } else if (release.current === null) { // .meteor/release says "none" but not in a checkout. - Console.stderr.write("none\n"); + Console.error("none"); return 1; } else { - Console.stdout.write(release.current.name + "\n"); - Console.stdout.write(files.getToolsVersion() + "\n"); + Console.info(release.current.name); + Console.info(files.getToolsVersion()); return 0; } }); @@ -194,16 +197,16 @@ function doRunCommand (options) { var parsedUrl = utils.parseUrl(options.port); } catch (err) { if (options.verbose) { - Console.stderr.write('Error while parsing --port option: ' - + err.stack + '\n'); + Console.rawError( + "Error while parsing --port option: " + err.stack); } else { - Console.stderr.write(err.message + '\n'); + Console.error(err.message); } return 1; } if (! parsedUrl.port) { - Console.stderr.write("--port must include a port.\n"); + Console.error("--port must include a port."); return 1; } @@ -211,10 +214,10 @@ function doRunCommand (options) { var parsedMobileServer = utils.mobileServerForRun(options); } catch (err) { if (options.verbose) { - Console.stderr.write('Error while parsing --mobile-server option: ' - + err.stack + '\n'); + Console.rawError( + "Error while parsing --mobile-server option: " + err.stack); } else { - Console.stderr.write(err.message + '\n'); + Console.error(err.message); } return 1; } @@ -290,14 +293,13 @@ function doRunCommand (options) { // If we are targeting the remote devices, warn about ports and same network if (utils.runOnDevice(options)) { cordova.verboseLog('A run on a device requested'); - var warning = [ -"WARNING: You are testing your app on a remote device.", -" For the mobile app to be able to connect to the local server, make", -" sure your device is on the same network, and that the network", -" configuration allows clients to talk to each other", -" (no client isolation)."]; - - Console.stderr.write(warning.join("\n")); + var warning = + "You are testing your app on a remote device." + + "For the mobile app to be able to connect to the local server, make " + + "sure your device is on the same network, and that the network " + + "configuration allows clients to talk to each other " + + "(no client isolation)."; + Console.labelWarn(warning); } @@ -305,9 +307,10 @@ function doRunCommand (options) { if (options['app-port']) { var appPortMatch = options['app-port'].match(/^(?:(.+):)?([0-9]+)?$/); if (!appPortMatch) { - Console.stderr.write( -"run: --app-port must be a number or be of the form 'host:port' where\n" + -"port is a number. Try 'meteor help run' for help.\n"); + Console.error( + "run: --app-port must be a number or be of the form 'host:port' ", + "where port is a number. Try", + Console.command("'meteor help run'") + " for help."); return 1; } appHost = appPortMatch[1] || null; @@ -379,8 +382,9 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { if (!options.appDir) { - Console.stderr.write( - "The 'meteor shell' command must be run in a Meteor app directory." + Console.error( + "The " + Console.command("'meteor shell'") + " command must be run", + "in a Meteor app directory." ); } else { require('./server/shell.js').connect(options.appDir); @@ -412,12 +416,13 @@ main.registerCommand({ // No package examples exist yet. if (options.list && options.example) { - Console.stderr.write("No package examples exist at this time.\n\n"); + Console.error("No package examples exist at this time."); + Console.error(); throw new main.ShowUsage; } if (!packageName) { - Console.stderr.write("Please specify the name of the package. \n"); + Console.error("Please specify the name of the package."); throw new main.ShowUsage; } @@ -430,7 +435,7 @@ main.registerCommand({ var inYourApp = options.appDir ? " in your app" : ""; if (fs.existsSync(packageDir)) { - Console.stderr.write(packageName + ": Already exists" + inYourApp + "\n"); + Console.error(packageName + ": Already exists" + inYourApp); return 1; } @@ -469,11 +474,11 @@ main.registerCommand({ ignore: [/^local$/] }); } catch (err) { - Console.stderr.write("Could not create package: " + err.message + "\n"); + Console.error("Could not create package: " + err.message); return 1; } - Console.stdout.write(packageName + ": created" + inYourApp + "\n"); + Console.info(packageName + ": created" + inYourApp); return 0; } @@ -499,12 +504,16 @@ main.registerCommand({ }); if (options.list) { - Console.stdout.write("Available examples:\n"); + Console.info("Available examples:"); _.each(examples, function (e) { - Console.stdout.write(" " + e + "\n"); + Console.info( + Console.command(e), + Console.options({ indent: 2 })); }); - Console.stdout.write("\n" + -"Create a project from an example with 'meteor create --example '.\n"); + Console.info(); + Console.info( + "Create a project from an example with " + + Console.command("'meteor create --example '") + "."); return 0; }; @@ -518,13 +527,13 @@ main.registerCommand({ var appPath = path.resolve(appPathAsEntered); if (fs.existsSync(appPath)) { - Console.stderr.write(appPath + ": Already exists\n"); + Console.error(appPath + ": Already exists"); return 1; } if (files.findAppDir(appPath)) { - Console.stderr.write( - "You can't create a Meteor project inside another Meteor project.\n"); + Console.error( + "You can't create a Meteor project inside another Meteor project."); return 1; } @@ -534,8 +543,11 @@ main.registerCommand({ if (options.example) { if (examples.indexOf(options.example) === -1) { - Console.stderr.write(options.example + ": no such example\n\n"); - Console.stderr.write("List available applications with 'meteor create --list'.\n"); + Console.error(options.example + ": no such example."); + Console.error(); + Console.error( + "List available applications with", + Console.command("'meteor create --list'") + "."); return 1; } else { files.cp_r(path.join(exampleDir, options.example), appPath, { @@ -598,10 +610,12 @@ main.registerCommand({ Console.info(message); } - Console.stdout.write( - "To run your new app:\n" + - " cd " + appPathAsEntered + "\n" + - " meteor\n"); + Console.info("To run your new app:"); + Console.info( + Console.command("cd " + appPathAsEntered), Console.options({ indent: 2 })); + Console.info( + Console.command("meteor"), Console.options({ indent: 2 })); + }); /////////////////////////////////////////////////////////////////////////////// @@ -639,11 +653,13 @@ main.registerCommand(_.extend({ name: 'bundle', hidden: true }, buildCommands), function (options) { - Console.stderr.write( -"This command has been deprecated in favor of 'meteor build', which allows you to\n" + -"build for multiple platforms and outputs a directory instead of a single\n" + -"tarball. See 'meteor help build' for more information.\n\n"); - + Console.error( + "This command has been deprecated in favor of " + + Console.command("'meteor build'") + ", which allows you to " + + "build for multiple platforms and outputs a directory instead of " + + "a single tarball. See " + Console.command("'meteor help build'") + + "for more information."); + Console.error();x return buildCommand(_.extend(options, { _serverOnly: true })); }); @@ -698,20 +714,20 @@ var buildCommand = function (options) { var parsedMobileServer = utils.parseUrl( mobileServer, { protocol: "http://" }); } catch (err) { - Console.stderr.write(err.message); + Console.error(err.message); return 1; } if (! parsedMobileServer.host) { - Console.stderr.write("--server must include a hostname.\n"); + Console.error("--server must include a hostname."); return 1; } } else { // For Cordova builds, require '--server'. // XXX better error message? - Console.stderr.write( -"Supply the server hostname and port in the --server option\n" + -"for mobile app builds.\n"); + Console.error( + "Supply the server hostname and port in the --server option " + + "for mobile app builds."); return 1; } var cordovaSettings = {}; @@ -743,11 +759,14 @@ var buildCommand = function (options) { // We would like the output path to be outside the app directory, which // means the first step to getting there is going up a level. if (relative.substr(0, 3) !== ('..' + path.sep)) { - Console.warn(""); + Console.warn(); Console.warn("Warning: The output directory is under your source tree."); - Console.warn(" Your generated files may get interpreted as source code!"); - Console.warn(" Consider building into a different directory instead (" + Console.command("meteor build ../output") + ")"); - Console.warn(""); + Console.warn( + "Your generated files may get interpreted as source code!", + "Consider building into a different directory instead (" + + Console.command("meteor build ../output") + ")", + Console.options({ indent: 2 })); + Console.warn(); } } @@ -776,8 +795,8 @@ var buildCommand = function (options) { } }); if (bundleResult.errors) { - Console.stderr.write("Errors prevented bundling:\n"); - Console.stderr.write(bundleResult.errors.formatMessages()); + Console.error("Errors prevented bundling:"); + Console.error(bundleResult.errors.formatMessages()); return 1; } @@ -791,8 +810,8 @@ var buildCommand = function (options) { files.createTarball(path.join(buildDir, 'bundle'), outputTar); } catch (err) { - Console.stderr.write("Errors during tarball creation:\n"); - Console.stderr.write(err.message); + Console.error("Errors during tarball creation:"); + Console.error(err.message); files.rm_recursive(buildDir); return 1; } @@ -869,17 +888,18 @@ main.registerCommand({ // specified? if (! mongoPort) { - Console.stdout.write( -"mongo: Meteor isn't running a local MongoDB server.\n" + -"\n" + -"This command only works while Meteor is running your application\n" + -"locally. Start your application first. (This error will also occur if\n" + -"you asked Meteor to use a different MongoDB server with $MONGO_URL when\n" + -"you ran your application.)\n" + -"\n" + -"If you're trying to connect to the database of an app you deployed\n" + -"with 'meteor deploy', specify your site's name with this command.\n" -); + Console.info("mongo: Meteor isn't running a local MongoDB server."); + Console.info(); + Console.info( + "This command only works while Meteor is running your application " + + "locally. Start your application first. (This error will also occur " + + "if you asked Meteor to use a different MongoDB server with " + + "$MONGO_URL when you ran your application.)"); + Console.info(); + Console.info( + "If you're trying to connect to the database of an app you deployed " + + "with " + Console.command("'meteor deploy'") + + ", specify your site's name with this command."); return 1; } mongoUrl = "mongodb://127.0.0.1:" + mongoPort + "/meteor"; @@ -926,13 +946,14 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { if (options.args.length !== 0) { - Console.stderr.write( -"meteor reset only affects the locally stored database.\n" + -"\n" + -"To reset a deployed application use\n" + -" meteor deploy --delete appname\n" + -"followed by\n" + -" meteor deploy appname\n"); + Console.error("meteor reset only affects the locally stored database."); + Console.error(); + Console.error("To reset a deployed application use"); + Console.error( + "meteor deploy --delete appname", Console.options({ indent: 2 })); + Console.error("followed by"); + Console.error( + "meteor deploy appname", Console.options({ indent: 2 })); return 1; } @@ -943,18 +964,18 @@ main.registerCommand({ require(path.join(__dirname, 'run-mongo.js')).findMongoPort; var isRunning = !! findMongoPort(options.appDir); if (isRunning) { - Console.stderr.write( -"reset: Meteor is running.\n" + -"\n" + -"This command does not work while Meteor is running your application.\n" + -"Exit the running Meteor development server.\n"); + Console.error("reset: Meteor is running."); + Console.error(); + Console.error( + "This command does not work while Meteor is running your application.", + "Exit the running Meteor development server."); return 1; } var localDir = path.join(options.appDir, '.meteor', 'local'); files.rm_recursive(localDir); - Console.stdout.write("Project reset.\n"); + Console.info("Project reset."); }); /////////////////////////////////////////////////////////////////////////////// @@ -1005,13 +1026,14 @@ main.registerCommand({ if (options.password) { if (useGalaxy) { - Console.stderr.write("Galaxy does not support --password.\n"); + Console.error("Galaxy does not support --password."); } else { - Console.stderr.write( -"Setting passwords on apps is no longer supported. Now there are\n" + -"user accounts and your apps are associated with your account so that\n" + -"only you (and people you designate) can access them. See the\n" + -"'meteor claim' and 'meteor authorized' commands.\n"); + Console.error( + "Setting passwords on apps is no longer supported. Now there are " + + "user accounts and your apps are associated with your account so " + + "that only you (and people you designate) can access them. See the " + + Console.command("'meteor claim'") + " and " + + Console.command("'meteor authorized'") + " commands."); } return 1; } @@ -1019,18 +1041,16 @@ main.registerCommand({ var starball = options.star; if (starball && ! useGalaxy) { // XXX it would be nice to support this for non-Galaxy deploys too - Console.stderr.write( -"--star: only supported when deploying to Galaxy.\n"); + Console.error("--star: only supported when deploying to Galaxy."); return 1; } var loggedIn = auth.isLoggedIn(); if (! loggedIn) { - Console.stderr.write( -"To instantly deploy your app on a free testing server, just enter your\n" + -"email address!\n" + -"\n"); - + Console.error( + "To instantly deploy your app on a free testing server,", + "just enter your email address!"); + Console.error(); if (! auth.registerOrLogIn()) return 1; } @@ -1038,10 +1058,12 @@ main.registerCommand({ // Override architecture iff applicable. var buildArch = DEPLOY_ARCH; if (options['override-architecture-with-local']) { - Console.stdout.write( - "\n => WARNING: OVERRIDING DEPLOY ARCHITECTURE WITH LOCAL ARCHITECTURE\n"); - Console.stdout.write( - " => If your app contains binary code, it may break terribly and you will be sad.\n\n"); + Console.warn(); + Console.labelWarn( + "OVERRIDING DEPLOY ARCHITECTURE WITH LOCAL ARCHITECTURE."); + Console.arrowInfo( + "If your app contains binary code, it may break in unexpected " + + "and terrible ways."); buildArch = archinfo.host(); } @@ -1143,14 +1165,15 @@ main.registerCommand({ }, function (options) { if (options.add && options.remove) { - Console.stderr.write( - "Sorry, you can only add or remove one user at a time.\n"); + Console.error( + "Sorry, you can only add or remove one user at a time."); return 1; } if ((options.add || options.remove) && options.list) { - Console.stderr.write( -"Sorry, you can't change the users at the same time as you're listing them.\n"); + Console.error( + "Sorry, you can't change the users at the same time as", + "you're listing them."); return 1; } @@ -1159,16 +1182,17 @@ main.registerCommand({ var site = qualifySitename(options.args[0]); if (hostedWithGalaxy(site)) { - Console.stderr.write( -"Sites hosted on Galaxy do not have an authorized user list.\n" + -"Instead, go to your Galaxy dashboard to change the authorized users\n" + -"of your Galaxy.\n"); + Console.error( + "Sites hosted on Galaxy do not have an authorized user list. " + + "Instead, go to your Galaxy dashboard to change the authorized users " + + "of your Galaxy.\n"); return 1; } if (! auth.isLoggedIn()) { - Console.stderr.write( - "You must be logged in for that. Try 'meteor login'.\n"); + Console.error( + "You must be logged in for that. Try " + + Console.command("'meteor login'")); return 1; } @@ -1195,16 +1219,19 @@ main.registerCommand({ var site = qualifySitename(options.args[0]); if (! auth.isLoggedIn()) { - Console.stderr.write( -"You must be logged in to claim sites. Use 'meteor login' to log in.\n" + -"If you don't have a Meteor developer account yet, create one by clicking\n" + -"'Sign in' and then 'Create account' at www.meteor.com.\n\n"); + Console.error( + "You must be logged in to claim sites. Use " + + Console.command("'meteor login'") + " to log in. If you don't have a " + + "Meteor developer account yet, create one by clicking " + + Console.command("'Sign in'") + " and then " + + Console.command("'Create account'") + " at www.meteor.com."); + Console.error(); return 1; } if (hostedWithGalaxy(site)) { - Console.stderr.write( - "Sorry, you can't claim sites that are hosted on Galaxy.\n"); + Console.error( + "Sorry, you can't claim sites that are hosted on Galaxy."); return 1; } @@ -1271,19 +1298,19 @@ main.registerCommand({ try { var parsedUrl = utils.parseUrl(options.port); } catch (err) { - Console.stderr.write(err.message); + Console.error(err.message); return 1; } if (! parsedUrl.port) { - Console.stderr.write("--port must include a port.\n"); + Console.error("--port must include a port."); return 1; } try { var parsedMobileServer = utils.mobileServerForRun(options); } catch (err) { - Console.stderr.write(err.message); + Console.error(err.message); return 1; } @@ -1584,18 +1611,18 @@ main.registerCommand({ var loggedInAccountsConnectionOrPrompt = function (action) { var token = auth.getSessionToken(config.getAccountsDomain()); if (! token) { - Console.stderr.write("You must be logged in to " + action + ".\n"); + Console.error("You must be logged in to " + action + "."); auth.doUsernamePasswordLogin({ retry: true }); - Console.stdout.write("\n"); + Console.info(); } token = auth.getSessionToken(config.getAccountsDomain()); var conn = auth.loggedInAccountsConnection(token); if (conn === null) { // Server rejected our token. - Console.stderr.write("You must be logged in to " + action + ".\n"); + Console.error("You must be logged in to " + action + "."); auth.doUsernamePasswordLogin({ retry: true }); - Console.stdout.write("\n"); + Console.info(); token = auth.getSessionToken(config.getAccountsDomain()); conn = auth.loggedInAccountsConnection(token); } @@ -1613,9 +1640,9 @@ main.registerCommand({ var token = auth.getSessionToken(config.getAccountsDomain()); if (! token) { - Console.stderr.write("You must be logged in to list your organizations.\n"); + Console.error("You must be logged in to list your organizations."); auth.doUsernamePasswordLogin({ retry: true }); - Console.stdout.write("\n"); + Console.info(); } var url = config.getAccountsApiUrl() + "/organizations"; @@ -1628,13 +1655,13 @@ main.registerCommand({ }); var body = JSON.parse(result.body); } catch (err) { - Console.stderr.write("Error listing organizations.\n"); + Console.error("Error listing organizations."); return 1; } if (result.response.statusCode === 401 && body && body.error === "invalid_credential") { - Console.stderr.write("You must be logged in to list your organizations.\n"); + Console.error("You must be logged in to list your organizations."); // XXX It would be nice to do a username/password prompt here like // we do for the other orgs commands. return 1; @@ -1642,14 +1669,14 @@ main.registerCommand({ if (result.response.statusCode !== 200 || ! body || ! body.organizations) { - Console.stderr.write("Error listing organizations.\n"); + Console.error("Error listing organizations."); return 1; } if (body.organizations.length === 0) { - Console.stdout.write("You are not a member of any organizations.\n"); + Console.info("You are not a member of any organizations."); } else { - Console.stdout.write(_.pluck(body.organizations, "name").join("\n") + "\n"); + Console.rawInfo(_.pluck(body.organizations, "name").join("\n")); } return 0; }); @@ -1667,8 +1694,8 @@ main.registerCommand({ }, function (options) { if (options.add && options.remove) { - Console.stderr.write( - "Sorry, you can only add or remove one member at a time.\n"); + Console.error( + "Sorry, you can only add or remove one member at a time."); throw new main.ShowUsage; } @@ -1686,28 +1713,26 @@ main.registerCommand({ options.add ? "addOrganizationMember": "removeOrganizationMember", options.args[0], username); } catch (err) { - Console.stderr.write("Error " + - (options.add ? "adding" : "removing") + - " member: " + err.reason + "\n"); + Console.error("Error " + + (options.add ? "adding" : "removing") + + " member: " + err.reason); return 1; } - Console.stdout.write(username + " " + + Console.info(username + " " + (options.add ? "added to" : "removed from") + - " organization " + options.args[0] + ".\n"); + " organization " + options.args[0] + "."); } else { // Showing the members of an org try { var result = conn.call("showOrganization", options.args[0]); } catch (err) { - Console.stderr.write("Error showing organization: " + - err.reason + "\n"); + Console.error("Error showing organization: " + err.reason); return 1; } var members = _.pluck(result, "username"); - - Console.stdout.write(members.join("\n") + "\n"); + Console.rawInfo(members.join("\n")); } return 0; @@ -1756,7 +1781,7 @@ main.registerCommand({ } catch (e) { if (!(e instanceof SyntaxError)) throw e; - Console.stderr.write("Bad regular expression: " + str + "\n"); + Console.error("Bad regular expression: " + str); return null; } }; @@ -1819,8 +1844,9 @@ main.registerCommand({ }, function (options) { auth.pollForRegistrationCompletion(); if (! auth.isLoggedIn()) { - Console.stderr.write( - "You must be logged in for that. Try 'meteor login'.\n"); + Console.error( + "You must be logged in for that. Try " + + Console.command("'meteor login'") + "."); return 1; } @@ -1899,7 +1925,7 @@ main.registerCommand({ 'key' : ret.sshKey, 'hostKey' : ret.hostKey }; - Console.info(JSON.stringify(retJson, null, 2)); + Console.rawInfo(JSON.stringify(retJson, null, 2)); return 0; } @@ -1927,7 +1953,7 @@ main.registerCommand({ maybeVerbose]; var printOptions = connOptions.join(' '); - maybeLog("Connecting: ssh " + printOptions); + maybeLog("Connecting: " + Console.command("ssh " + printOptions)); var child_process = require('child_process'); var future = new Future; @@ -1979,10 +2005,10 @@ main.registerCommand({ return 'none'; }; - Console.stdout.write(p('email') + " " + p('port') + " " + p('changed') + - " " + p('args') + "\n"); + Console.info(p('email') + " " + p('port') + " " + p('changed') + + " " + p('args')); if (options.url) - Console.stdout.write('url\n'); + Console.info('url'); if (options['delete']) - Console.stdout.write('delete\n'); + Console.info('delete'); }); diff --git a/tools/console.js b/tools/console.js index 3f729e0588..de734921a7 100644 --- a/tools/console.js +++ b/tools/console.js @@ -1,17 +1,19 @@ /// -/// utility functions for formatting output to the screen +/// A set of utility functions for formatting output sent to the screen. /// /// Console offers several pieces of functionality: -/// debug / info / warn messages: -/// Outputs to the screen, optionally with colors (when pretty == true) -/// 'legacy' functions: Console.stdout.write & Console.stderr.write -/// Make porting code a lot easier (just a regex from process -> Console) -/// Progress bar support -/// Displays a progress bar on the screen, but hides it around log messages -/// (The need to hide it is why we have this class) +/// - debug / info / warn messages: Output to the screen, optionally with +/// colors (when pretty == true). Wrap the output to the width of the user's +/// terminal, making sure to not split the same word over multiple +/// lines. (Also provides 'rawInfo', 'rawDebug' (etc) for when you DON'T want +/// to pre-process the output.) +/// - Progress bar support +/// Display a progress bar on the screen, but hide it around log messages. +/// - 'legacy' functions: Console.stdout.write & Console.stderr.write +/// Make porting code a lot easier (just a regex from process -> Console) /// -/// In future, we might do things like move all support for verbose mode in here, -/// and also integrate the buildmessage functionality into here +/// In future, we might do things like move all support for verbose mode in +/// here, and also integrate the buildmessage functionality into here /// var _ = require('underscore'); @@ -24,6 +26,7 @@ var buildmessage = require('./buildmessage.js'); var chalk = require('chalk'); var cleanup = require('./cleanup.js'); var utils = require('./utils.js'); +var wordwrap = require('wordwrap'); var PROGRESS_DEBUG = !!process.env.METEOR_PROGRESS_DEBUG; var FORCE_PRETTY=undefined; @@ -31,11 +34,10 @@ if (process.env.METEOR_PRETTY_OUTPUT) { FORCE_PRETTY = process.env.METEOR_PRETTY_OUTPUT != '0'; } -if (!process.env.METEOR_COLOR) { +if (! process.env.METEOR_COLOR) { chalk.enabled = false; } - var STATUSLINE_MAX_LENGTH = 60; // XXX unused? var STATUS_MAX_LENGTH = 40; @@ -49,6 +51,18 @@ var STATUS_INTERVAL_MS = 500; // XXX: ? FALLBACK_STATUS = 'Pondering'; var FALLBACK_STATUS = ''; +// If there is a part of the larger text, and we really want to make sure that +// it doesn't get split up, we will replace the space with a utf character that +// we are not likely to use anywhere else. This one looks like the sun! We +// intentionally want to NOT use a space-like character: it should be obvious +// that something has gone wrong if this ever gets printed. +var SPACE_REPLACEMENT = '\u2600'; +// In Javascript, replace only replaces the first occurance and this is the +// proposed alternative. +var replaceAll = function (str, search, replace) { + return str.split(search).join(replace); +}; + var spacesArray = new Array(200).join(' '); var spacesString = function (length) { if (length > spacesArray.length) { @@ -56,6 +70,8 @@ var spacesString = function (length) { } return spacesArray.substring(0, length); }; +var ARROW = "=> "; + var toFixedLength = function (text, length) { text = text || ""; @@ -72,7 +88,8 @@ var toFixedLength = function (text, length) { return text; }; -// No-op progress display, that means we don't have to handle the 'no progress display' case +// No-op progress display, that means we don't have to handle the 'no progress +// display' case var ProgressDisplayNone = function () { }; @@ -217,7 +234,7 @@ _.extend(ProgressBarRenderer.prototype, { .replace(':current', self.curr) .replace(':total', self.total) .replace(':elapsed', isNaN(elapsed) ? '0.0' : (elapsed / 1000).toFixed(1)) - .replace(':eta', (isNaN(eta) || !isFinite(eta)) ? '0.0' : (eta / 1000).toFixed(1)) + .replace(':eta', (isNaN(eta) || ! isFinite(eta)) ? '0.0' : (eta / 1000).toFixed(1)) .replace(':percent', percent.toFixed(0) + '%'); /* compute the available space (non-zero) for the bar */ @@ -312,7 +329,7 @@ _.extend(ProgressDisplayFull.prototype, { var streamColumns = this._stream.columns; var statusColumns; var progressColumns; - if (!streamColumns) { + if (! streamColumns) { statusColumns = STATUS_MAX_LENGTH; progressColumns = 0; } else { @@ -377,7 +394,7 @@ _.extend(StatusPoller.prototype, { } self._pollFiber = Fiber(function () { - while (!self._stop) { + while (! self._stop) { utils.sleepMs(100); self.statusPoll(); @@ -414,7 +431,7 @@ _.extend(StatusPoller.prototype, { } else { var fraction = state.done ? 1.0 : (state.current / state.end); - if (!isNaN(fraction) && fraction >= 0) { + if (! isNaN(fraction) && fraction >= 0) { progressDisplay.updateProgress(fraction, startTime); } else { progressDisplay.updateProgress(0, startTime); @@ -508,6 +525,15 @@ var LEVEL_WARN = { code: LEVEL_CODE_WARN }; var LEVEL_INFO = { code: LEVEL_CODE_INFO }; var LEVEL_DEBUG = { code: LEVEL_CODE_DEBUG }; +// We use a special class to represent the options that we send to the Console +// because it allows us to call 'instance of' on the last argument of variadic +// functions. This allows us to keep the signature of our custom output +// functions (ex: info) roughly the same as the originals. +var ConsoleOptions = function (o) { + var self = this; + self.options = o; +} + _.extend(Console.prototype, { LEVEL_ERROR: LEVEL_ERROR, LEVEL_WARN: LEVEL_WARN, @@ -537,7 +563,7 @@ _.extend(Console.prototype, { self._pretty = self._progressDisplayEnabled = true; // Update the screen if anything changed. - if (!originalPretty || !originalProgressDisplayEnabled) + if (! originalPretty || ! originalProgressDisplayEnabled) self._updateProgressDisplay(); try { @@ -547,7 +573,7 @@ _.extend(Console.prototype, { self._pretty = originalPretty; self._progressDisplayEnabled = originalProgressDisplayEnabled; // Update the screen if anything changed. - if (!originalPretty || !originalProgressDisplayEnabled) + if (! originalPretty || ! originalProgressDisplayEnabled) self._updateProgressDisplay(); } }, @@ -557,6 +583,16 @@ _.extend(Console.prototype, { self.verbose = verbose; }, + // Get the current width of the Console. + width: function () { + var width = 80; + var stream = process.stdout; + if (stream && stream.isTTY && stream.columns) { + width = stream.columns; + } + return width; + }, + // This can be called during long lived operations; it will keep the spinner spinning. // (This code used to be in Patience.nudge) // @@ -583,6 +619,38 @@ _.extend(Console.prototype, { } }, + // Initializes and returns a new ConsoleOptions object. This allows us to call + // 'instance of' on the ConsoleOptions in parseVariadicInput, by ensuring that + // the object created with Console.options is, in fact, a new object. + options: function (o) { + return new ConsoleOptions(o); + }, + + // Deal with the arguments to a variadic print function that also takes an + // optional ConsoleOptions argument at the end. + // + // Returns an object with keys: + // - opts: The options that were passed in, or an empty object. + // - message: Arguments to the original function, parsed as a string. + // + _parseVariadicInput: function (args) { + var self = this; + var msgArgs; + var opts; + // If the last argument is an instance of ConsoleOptions, then we should + // separate it out, and only send the first N-1 arguments to be parsed as a + // message. + if (_.last(args) instanceof ConsoleOptions) { + msgArgs = _.initial(args); + opts = _.last(args).options; + } else { + msgArgs = args; + opts = {}; + } + var message = self._format(msgArgs); + return { message: message, opts: opts }; + }, + isLevelEnabled: function (levelCode) { return (this.verbose || this._logThreshold <= levelCode); }, @@ -591,9 +659,12 @@ _.extend(Console.prototype, { return this.isLevelEnabled(LEVEL_CODE_DEBUG); }, - debug: function(/*arguments*/) { + + // Don't pretty-fy this output by trying to, for example, line-wrap it. Just + // print it to the screen as it is. + rawDebug: function(/*arguments*/) { var self = this; - if (!self.isDebugEnabled()) { + if (! self.isDebugEnabled()) { return; } @@ -601,13 +672,38 @@ _.extend(Console.prototype, { self._print(LEVEL_DEBUG, message); }, + // By default, Console.debug automatically line wrapps the output. + // + // Takes in an optional Console.options({}) argument at the end, with the + // following keys: + // - bulletPoint: start the first line with a given string, then offset the + // subsequent lines by the length of that string. See _wrap for more details. + // - indent: offset the entire string by a specific number of + // characters. See _wrap for more details. + // + debug: function(/*arguments*/) { + var self = this; + if (! self.isDebugEnabled()) { return; } + + var parsedArgs = self._parseVariadicInput(arguments); + var wrapOpts = { + indent: parsedArgs.opts.indent, + bulletPoint: parsedArgs.opts.bulletPoint + }; + + var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); + self._print(LEVEL_DEBUG, wrappedMessage); + }, + isInfoEnabled: function () { return this.isLevelEnabled(LEVEL_CODE_INFO); }, - info: function(/*arguments*/) { + // Don't pretty-fy this output by trying to, for example, line-wrap it. Just + // print it to the screen as it is. + rawInfo: function(/*arguments*/) { var self = this; - if (!self.isInfoEnabled()) { + if (! self.isInfoEnabled()) { return; } @@ -615,13 +711,30 @@ _.extend(Console.prototype, { self._print(LEVEL_INFO, message); }, + // Generally, we want to process the output for legibility, for example, by + // wrapping it. For raw output (ex: stack traces, user logs, etc), use the + // rawInfo function. For more information about options, see: debug. + info: function(/*arguments*/) { + var self = this; + if (! self.isInfoEnabled()) { return; } + + var parsedArgs = self._parseVariadicInput(arguments); + var wrapOpts = { + indent: parsedArgs.opts.indent, + bulletPoint: parsedArgs.opts.bulletPoint + }; + + var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); + self._print(LEVEL_INFO, wrappedMessage); + }, + isWarnEnabled: function () { return this.isLevelEnabled(LEVEL_CODE_WARN); }, - warn: function(/*arguments*/) { + rawWarn: function(/*arguments*/) { var self = this; - if (!self.isWarnEnabled()) { + if (! self.isWarnEnabled()) { return; } @@ -629,13 +742,46 @@ _.extend(Console.prototype, { self._print(LEVEL_WARN, message); }, - error: function(/*arguments*/) { + // Generally, we want to process the output for legibility, for example, by + // wrapping it. For raw output (ex: stack traces, user logs, etc), use the + // rawWarn function. For more information about options, see: debug. + warn: function(/*arguments*/) { + var self = this; + if (! self.isWarnEnabled()) { return; } + + var parsedArgs = self._parseVariadicInput(arguments); + var wrapOpts = { + indent: parsedArgs.opts.indent, + bulletPoint: parsedArgs.opts.bulletPoint + }; + + var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); + self._print(LEVEL_WARN, wrappedMessage); + }, + + rawError: function(/*arguments*/) { var self = this; var message = self._format(arguments); self._print(LEVEL_ERROR, message); }, + // Generally, we want to process the output for legibility, for example, by + // wrapping it. For raw output (ex: stack traces, user logs, etc), use the + // rawWarn function. For more information about options, see: debug. + error: function(/*arguments*/) { + var self = this; + + var parsedArgs = self._parseVariadicInput(arguments); + var wrapOpts = { + indent: parsedArgs.opts.indent, + bulletPoint: parsedArgs.opts.bulletPoint + }; + + var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); + self._print(LEVEL_ERROR, wrappedMessage); + }, + _legacyWrite: function (level, message) { var self = this; if(message.substr && message.substr(-1) == '\n') { @@ -684,64 +830,92 @@ _.extend(Console.prototype, { dest.write(message + '\n'); } - // XXX: Pause before showing the progress display, to prevent flicker/spewing messages + // XXX: Pause before showing the progress display, to prevent + // flicker/spewing messages // Repaint the progress display progressDisplay.repaint(); }, + // A wrapper around Console.info. Prints the message out in green (if pretty), + // with the ascii checkmark as the bullet point in front of it. success: function (message) { var self = this; - if (!self._pretty) { - return message; + if (! self._pretty) { + return self.info(message); } - return chalk.green('\u2713 ' + message); // CHECK MARK + + var checkmark = chalk.green('\u2713'); + return self.info( + chalk.green(message), + self.options({ bulletPoint: checkmark })); }, - fail: function (message) { + // Wrapper around Console.info. Prints the message out in red (if pretty) + // with the ascii x as the bullet point in front of it. + failInfo: function (message) { + var self = this; + return self._fail(message, self.info); + }, + + // Wrapper around Console.warn. Prints the message out in red (if pretty) + // with the ascii x as the bullet point in front of it. + failWarn: function (message) { + var self = this; + return self._fail(message, self.warn); + }, + + // Print the message in red (if pretty) with an x bullet point in front of it. + _fail: function (message, printFn) { var self = this; - if (!self._pretty) { - return message; + if (! self._pretty) { + return printFn(message); } - return chalk.red('\u2717 ' + message); // BALLOT X + + var xmark = chalk.red('\u2717'); + return printFn( + chalk.red(message), + self.options({ bulletPoint: xmark })); }, - command: function (message) { - return this.bold(message); - }, - - url: function (message) { - return this.underline(message); - }, - - underline: function (message) { + // Wrapper around Console.warn that prints a large "WARNING" label in front. + labelWarn: function (message) { var self = this; - - if (!self._pretty) { - return message; - } - return chalk.underline(message); + return self.warn(message, self.options({ bulletPoint: "WARNING" })); }, - bold: function (message) { + // Wrappers around Console functions to prints an "=> " in front. Optional + // indent to indent the arrow. + arrowError: function (message, indent) { var self = this; - - if (!self._pretty) { - return message; - } - return chalk.bold(message); - }, - - _format: function (logArguments) { - return util.format.apply(util, logArguments); + return self._arrowPrint("error", message, indent); + }, + arrowWarn: function (message, indent) { + var self = this; + return self._arrowPrint("warn", message, indent); + }, + arrowInfo: function (message, indent) { + var self = this; + return self._arrowPrint("info", message, indent); + }, + _arrowPrint: function(printFn, message, indent) { + var self = this; + indent = indent || 0; + var myIndent = Array(indent + 1).join(" "); + return self[printFn]( + message, + self.options({ bulletPoint: myIndent + ARROW })); }, + // A wrapper around console.error. Given an error and some background + // information, print out the correct set of messages depending on verbose + // level, etc. printError: function (err, info) { var self = this; var message = err.message; - if (!message) { + if (! message) { message = "Unexpected error"; if (self.verbose) { message += " (" + err.toString() + ")"; @@ -754,18 +928,160 @@ _.extend(Console.prototype, { self.error(message); if (self.verbose && err.stack) { - self.info(err.stack); + self.rawInfo(err.stack); } }, + // A wrapper to print out buildmessage errors. printMessages: function (messages) { var self = this; if (messages.hasMessages()) { - self._print(LEVEL_ERROR, "\n" + messages.formatMessages()); + self.error("\n" + messages.formatMessages()); } }, + // Wrap commands in this function -- it ensures that commands don't get line + // wrapped (ie: print 'meteor' at the end of the line, and 'create --example' + // at the beginning of the next one). + // + // To use, wrap commands that you send into print functions with this + // function, like so: Console.info(text + Console.command("meteor create + // --example leaderboard") + moretext). + // + // If pretty print is on, this will also bold the commands. + command: function (message) { + var self = this; + var noBlanks = + replaceAll(message, ' ', SPACE_REPLACEMENT); + return this.bold(noBlanks); + }, + + // Underline the URLs (if pretty print is on). + url: function (message) { + return this.underline(message); + }, + + // A wrapper around the underline functionality of chalk. + underline: function (message) { + var self = this; + + if (! self._pretty) { + return message; + } + return chalk.underline(message); + }, + + // A wrapper around the bold functionality of chalk. + bold: function (message) { + var self = this; + + if (! self._pretty) { + return message; + } + return chalk.bold(message); + }, + + // Prints a two column table in a nice format: + // The first column is printed entirely, the second only as space permits + printTwoColumns : function (rows, options) { + var self = this; + options = options || {}; + + var longest = ''; + _.each(rows, function (row) { + var col0 = row[0] || ''; + if (col0.length > longest.length) + longest = col0; + }); + + var pad = longest.replace(/./g, ' '); + var width = self.width(); + + var out = ''; + _.each(rows, function (row) { + var col0 = row[0] || ''; + var col1 = row[1] || ''; + var line = self.bold(col0) + pad.substr(col0.length); + line += " " + col1; + if (line.length > width) { + line = line.substr(0, width - 3) + '...'; + } + out += line + "\n"; + }); + + var level = options.level || self.LEVEL_INFO; + self._print(level, out); + + return out; + }, + + // Format logs according to the spec in utils. + _format: function (logArguments) { + return util.format.apply(util, logArguments); + }, + + // Wraps long strings to the length of user's terminal. Inserts linebreaks + // between words when nearing the end of the line. Returns the wrapped string + // and takes the following arguments: + // + // text: the text to wrap + // options: + // - bulletPoint: start the first line with a given string, then offset the + // subsequent lines by the length of that string. For example: + // " => some long message starts here + // and then continues here." + // - indent: offset the entire string by a specific number of + // characters. For example: + // " This entire message is indented + // by two characters." + // + // Passing in both options will offset the bulletPoint by the indentation, + // like so: + // " this message is indented by two." + // " => this mesage indented by two and + // and also starts with an arrow." + // + // When printing commands in-line, it is best to wrap commands in with Console.command + // to make sure that they don't get line-wrapped. See Console.command for more details. + _wrapText: function (text, options) { + var self = this; + options = options || {}; + + // Compute the maximum offset on the bulk of the message. + var maxIndent = 0; + if (options.indent && options.indent > 0) { + maxIndent = maxIndent + options.indent; + } + if (options.bulletPoint) { + maxIndent = maxIndent + options.bulletPoint.length; + } + + // Get the maximum width, or if we are not running in a terminal (self-test, + // for exmaple), default to 80 columns. + var max = self.width(); + + // Wrap the text using the npm wordwrap library. + var wrappedText = wordwrap(maxIndent, max)(text); + + // Insert the start string, if applicable. + if (options.bulletPoint) { + // Save the initial indent level. + var initIndent = options.indent ? + wrappedText.substring(0, options.indent) : ""; + // Add together the initial indent (if any), the bullet point and the + // remainder of the message. + wrappedText = initIndent + options.bulletPoint + + wrappedText.substring(maxIndent); + } + + // If we have previously replaces any spaces, now is the time to bring them + // back. + wrappedText = replaceAll(wrappedText, SPACE_REPLACEMENT, ' '); + return wrappedText; + }, + + // Enables the progress bar, or disables it when called with (false) enableProgressDisplay: function (enabled) { var self = this; @@ -789,12 +1105,12 @@ _.extend(Console.prototype, { var newProgressDisplay; - if (!self._progressDisplayEnabled) { + if (! self._progressDisplayEnabled) { newProgressDisplay = new ProgressDisplayNone(); - } else if ((!self._stream.isTTY) || (!self._pretty)) { + } else if ((! self._stream.isTTY) || (! self._pretty)) { // No progress bar if not in pretty / on TTY. newProgressDisplay = new ProgressDisplayNone(self); - } else if (self._stream.isTTY && !self._stream.columns) { + } else if (self._stream.isTTY && ! self._stream.columns) { // We might be in a pseudo-TTY that doesn't support // clearLine() and cursorTo(...). // It's important that we only enter status message mode @@ -809,7 +1125,7 @@ _.extend(Console.prototype, { // Start/stop the status poller, so we never block exit if (self._progressDisplayEnabled) { - if (!self._statusPoller) { + if (! self._statusPoller) { self._statusPoller = new StatusPoller(self); } } else { @@ -834,8 +1150,6 @@ _.extend(Console.prototype, { } }); -Console.prototype.warning = Console.prototype.warn; - // options: // - echo (boolean): defaults to true // - prompt (string) diff --git a/tools/deploy-galaxy.js b/tools/deploy-galaxy.js index 173092d20a..e139a82918 100644 --- a/tools/deploy-galaxy.js +++ b/tools/deploy-galaxy.js @@ -13,6 +13,7 @@ var _ = require('underscore'); var buildmessage = require('./buildmessage.js'); var ServiceConnection = require('./service-connection.js'); var stats = require('./stats.js'); +var Console = require('./console.js').Console; // If 'error' is an exception that we know how to report in a // user-friendly way, print an approprite message to stderr and return @@ -27,9 +28,9 @@ var handleError = function (error, galaxyName, messages) { if (error.errorType === "Meteor.Error") { var msg = messages[error.error]; if (msg) - process.stderr.write(msg + "\n"); + Console.error(msg); else if (error.message) - process.stderr.write("Denied: " + error.message + "\n"); + Console.error("Denied: " + error.message); return 1; } else if (error.errorType === "DDP.ConnectionError") { // If we have an http/https URL for a galaxyName instead of a @@ -39,7 +40,7 @@ var handleError = function (error, galaxyName, messages) { if (m) galaxyName = m[1]; - process.stderr.write(galaxyName + ": connection failed\n"); + Console.error(galaxyName + ": connection failed"); return 1; } else { throw error; @@ -147,7 +148,7 @@ exports.deleteApp = function (app) { try { conn.call("destroyApp", app); - process.stdout.write("Deleted.\n"); + Console.info("Deleted."); } catch (e) { return handleError(e, galaxy); } finally { @@ -193,7 +194,7 @@ exports.deploy = function (options) { // concurrent with bundling. if (! options.starball && ! messages.hasMessages()) { - process.stdout.write('Deploying ' + options.app + '. Bundling...\n'); + Console.info('Deploying ' + options.app + '. Bundling...'); var bundleResult = bundler.bundle({ projectContext: options.projectContext, outputPath: bundlePath, @@ -230,12 +231,12 @@ exports.deploy = function (options) { } if (messages.hasMessages()) { - process.stdout.write("\nErrors prevented deploying:\n"); - process.stdout.write(messages.formatMessages()); + Console.info("\nErrors prevented deploying:"); + Console.info(messages.formatMessages()); return 1; } - process.stdout.write('Uploading...\n'); + Console.info('Uploading...'); var galaxy = exports.discoverGalaxy(options.app); conn = galaxyServiceConnection(galaxy, "ultraworld"); @@ -289,11 +290,11 @@ exports.deploy = function (options) { if (error || ((response.statusCode !== 200) && (response.statusCode !== 201))) { if (error && error.message) - process.stderr.write("Upload failed: " + error.message + "\n"); + Console.error("Upload failed: " + error.message); else - process.stderr.write("Upload failed" + - (response.statusCode ? - " (" + response.statusCode + ")\n" : "\n")); + Console.error("Upload failed" + + (response.statusCode ? + " (" + response.statusCode + ")" : "")); future['return'](false); } else future['return'](true); @@ -313,10 +314,10 @@ exports.deploy = function (options) { } if (created) - process.stderr.write(options.app + ": created app\n"); + Console.error(options.app + ": created app\n"); - process.stderr.write(options.app + ": " + - "pushed revision " + result.serial + "\n"); + Console.error(options.app + ": " + + "pushed revision " + result.serial); return 0; } finally { // Close the connection to Galaxy (otherwise Node will continue running). diff --git a/tools/deploy.js b/tools/deploy.js index 3696cb5bbd..91f68ce7e7 100644 --- a/tools/deploy.js +++ b/tools/deploy.js @@ -256,12 +256,15 @@ var authedRpc = function (options) { // password-protected app, instruct them to claim it with 'meteor // claim'. var printLegacyPasswordMessage = function (site) { - Console.stderr.write( -"\nThis site was deployed with an old version of Meteor that used\n" + -"site passwords instead of user accounts. Now we have a much better\n" + -"system, Meteor developer accounts.\n\n" + -"If this is your site, please claim it into your account with\n" + -" meteor claim " + site + "\n"); + Console.error( + "\nThis site was deployed with an old version of Meteor that used " + + "site passwords instead of user accounts. Now we have a much better " + + "system, Meteor developer accounts."); + Console.error(); + Console.error("If this is your site, please claim it into your account with"); + Console.error( + Console.command("meteor claim " + site), + Console.options({ indent: 2 })); }; // When the user is trying to do something with an app that they are not @@ -269,12 +272,16 @@ var printLegacyPasswordMessage = function (site) { // --add' or switch accounts. var printUnauthorizedMessage = function () { var username = auth.loggedInUsername(); - Console.stderr.write( -"Sorry, that site belongs to a different user.\n" + -(username ? "You are currently logged in as " + username + ".\n" : "") + -"\nEither have the site owner use 'meteor authorized --add' to add you\n" + -"as an authorized developer for the site, or switch to an authorized\n" + -"account with 'meteor login'.\n"); + Console.error("Sorry, that site belongs to a different user."); + if (username) { + Console.error("You are currently logged in as " + username + "."); + } + Console.error(); + Console.error( + "Either have the site owner use " + + Console.command("'meteor authorized --add'") + " to add you as an " + + "authorized developer for the site, or switch to an authorized account " + + "with " + Console.command("'meteor login'") + "."); }; // Take a proposed sitename for deploying to. If it looks @@ -290,10 +297,10 @@ var canonicalizeSite = function (site) { // characters (url.parse will do something very strange if a component is // larger than 63, which is the maximum legal length). if (site.length > 63) { - Console.stdout.write( -"The maximum hostname length currently supported is 63 characters.\n" + -site + " is too long.\n" + -"Please try again with a shorter URL for your site.\n"); + Console.error( + "The maximum hostname length currently supported is 63 characters: " + + site + " is too long. " + + "Please try again with a shorter URL for your site."); return false; } @@ -304,16 +311,16 @@ site + " is too long.\n" + var parsed = require('url').parse(url); if (! parsed.hostname) { - Console.stdout.write( -"Please specify a domain to connect to, such as www.example.com or\n" + -"http://www.example.com/\n"); + Console.info( + "Please specify a domain to connect to, such as www.example.com or " + + "http://www.example.com/"); return false; } if (parsed.pathname != '/' || parsed.hash || parsed.query) { - Console.stdout.write( -"Sorry, Meteor does not yet support specific path URLs, such as\n" + -"http://www.example.com/blog . Please specify the root of a domain.\n"); + Console.info( + "Sorry, Meteor does not yet support specific path URLs, such as " + + "http://www.example.com/blog . Please specify the root of a domain."); return false; } @@ -364,14 +371,13 @@ var bundleAndDeploy = function (options) { }); if (preflight.errorMessage) { - Console.stderr.write("\nError deploying application: " + - preflight.errorMessage + "\n"); + Console.error("Error deploying application: " + preflight.errorMessage); return 1; } if (preflight.protection === "password") { printLegacyPasswordMessage(site); - Console.stderr.write("If it's not your site, please try a different name!\n"); + Console.error("If it's not your site, please try a different name!"); return 1; } else if (preflight.protection === "account" && @@ -383,7 +389,7 @@ var bundleAndDeploy = function (options) { var buildDir = options.projectContext.getProjectLocalDirectory('build_tar'); var bundlePath = path.join(buildDir, 'bundle'); - Console.stdout.write('Deploying to ' + site + '.\n'); + Console.info('Deploying to ' + site + '.'); var settings = null; var messages = buildmessage.capture({ @@ -408,8 +414,8 @@ var bundleAndDeploy = function (options) { } if (messages.hasMessages()) { - Console.stdout.write("\nErrors prevented deploying:\n"); - Console.stdout.write(messages.formatMessages()); + Console.info("\nErrors prevented deploying:"); + Console.info(messages.formatMessages()); return 1; } @@ -435,15 +441,14 @@ var bundleAndDeploy = function (options) { if (result.errorMessage) { - Console.stderr.write("\nError deploying application: " + - result.errorMessage + "\n"); + Console.error("\nError deploying application: " + result.errorMessage); return 1; } var deployedAt = require('url').parse(result.payload.url); var hostname = deployedAt.hostname; - Console.stdout.write('Now serving at http://' + hostname + '\n'); + Console.info('Now serving at http://' + hostname); files.rm_recursive(buildDir); if (! hostname.match(/meteor\.com$/)) { @@ -452,11 +457,12 @@ var bundleAndDeploy = function (options) { if (err || cnames[0] !== 'origin.meteor.com') { dns.resolve(hostname, 'A', function (err, addresses) { if (err || addresses[0] !== '107.22.210.133') { - Console.stdout.write('-------------\n'); - Console.stdout.write("You've deployed to a custom domain.\n"); - Console.stdout.write("Please be sure to CNAME your hostname to origin.meteor.com,\n"); - Console.stdout.write("or set an A record to 107.22.210.133.\n"); - Console.stdout.write('-------------\n'); + Console.info('-------------'); + Console.info( + "You've deployed to a custom domain.", + "Please be sure to CNAME your hostname", + "to origin.meteor.com, or set an A record to 107.22.210.133."); + Console.info('-------------'); } }); } @@ -479,12 +485,11 @@ var deleteApp = function (site) { }); if (result.errorMessage) { - Console.stderr.write("Couldn't delete application: " + - result.errorMessage + "\n"); + Console.error("Couldn't delete application: " + result.errorMessage); return 1; } - Console.stdout.write("Deleted.\n"); + Console.info("Deleted."); return 0; }; @@ -505,8 +510,7 @@ var checkAuthThenSendRpc = function (site, operation, what) { }); if (preflight.errorMessage) { - Console.stderr.write("Couldn't " + what + ": " + - preflight.errorMessage + "\n"); + Console.error("Couldn't " + what + ": " + preflight.errorMessage); return null; } @@ -530,15 +534,17 @@ var checkAuthThenSendRpc = function (site, operation, what) { } else { // Shouldn't ever get here because we set the retry flag on the // login, but just in case. - Console.stderr.write( -"\nYou must be logged in to " + what + " for this app. Use 'meteor login'\n" + -"to log in.\n\n" + -"If you don't have a Meteor developer account yet, you can quickly\n" + -"create one at www.meteor.com.\n"); + Console.error( + "\nYou must be logged in to " + what + " for this app. Use " + + Console.command("'meteor login'") + "to log in."); + Console.error(); + Console.error( + "If you don't have a Meteor developer account yet, you can quickly " + + "create one at www.meteor.com."); return null; } } else { // User is logged in but not authorized for this app - Console.stderr.write("\n"); + Console.error(); printUnauthorizedMessage(); return null; } @@ -554,8 +560,7 @@ var checkAuthThenSendRpc = function (site, operation, what) { }); if (result.errorMessage) { - Console.stderr.write("Couldn't " + what + ": " + - result.errorMessage + "\n"); + Console.error("Couldn't " + what + ": " + result.errorMessage); return null; } @@ -590,7 +595,7 @@ var logs = function (site) { if (result === null) { return 1; } else { - Console.stdout.write(result.message); + Console.info(result.message); auth.maybePrintRegistrationLink({ leadingNewline: true }); return 0; } @@ -607,33 +612,35 @@ var listAuthorized = function (site) { expectPayload: [] }); if (result.errorMessage) { - Console.stderr.write("Couldn't get authorized users list: " + - result.errorMessage + "\n"); + Console.error("Couldn't get authorized users list: " + result.errorMessage); return 1; } var info = result.payload; if (! _.has(info, 'protection')) { - Console.stdout.write("\n"); + Console.info(""); return 0; } if (info.protection === "password") { - Console.stdout.write("\n"); + Console.info(""); return 0; } if (info.protection === "account") { if (! _.has(info, 'authorized')) { - Console.stderr.write("Couldn't get authorized users list: " + - "You are not authorized\n"); + Console.error("Couldn't get authorized users list: " + + "You are not authorized"); return 1; } - Console.stdout.write((auth.loggedInUsername() || "") + "\n"); + Console.info((auth.loggedInUsername() || "")); _.each(info.authorized, function (username) { if (username) - Console.stdout.write(username + "\n"); + // Current username rules don't let you register anything that we might + // want to split over multiple lines (ex: containing a space), but we + // don't want confusion if we ever change some implementation detail. + Console.rawInfo(username + "\n"); }); return 0; } @@ -655,14 +662,13 @@ var changeAuthorized = function (site, action, username) { }); if (result.errorMessage) { - Console.stderr.write("Couldn't change authorized users: " + - result.errorMessage + "\n"); + Console.error("Couldn't change authorized users: " + result.errorMessage); return 1; } - Console.stdout.write(site + ": " + - (action === "add" ? "added " : "removed ") - + username + "\n"); + Console.info(site + ": " + + (action === "add" ? "added " : "removed ") + + username); return 0; }; @@ -679,27 +685,28 @@ var claim = function (site) { operation: 'info', site: site }); - if (infoResult.statusCode === 404) { - Console.stderr.write( -"There isn't a site deployed at that address. Use 'meteor deploy' if\n" + -"you'd like to deploy your app here.\n"); + Console.error( + "There isn't a site deployed at that address. Use " + + Console.command("'meteor deploy'") + " " + + "if you'd like to deploy your app here."); return 1; } if (infoResult.payload && infoResult.payload.protection === "account") { if (infoResult.payload.authorized) - Console.stderr.write("That site already belongs to you.\n"); + Console.error("That site already belongs to you.\n"); else - Console.stderr.write("Sorry, that site belongs to someone else.\n"); + Console.error("Sorry, that site belongs to someone else.\n"); return 1; } if (infoResult.payload && infoResult.payload.protection === "password") { - Console.stdout.write( -"To claim this site and transfer it to your account, enter the\n" + -"site password one last time.\n\n"); + Console.info( + "To claim this site and transfer it to your account, enter the", + "site password one last time."); + Console.info(); } var result = authedRpc({ @@ -713,29 +720,34 @@ var claim = function (site) { auth.pollForRegistrationCompletion(); if (! auth.loggedInUsername() && auth.registrationUrl()) { - Console.stderr.write( -"You need to set a password on your Meteor developer account before\n" + -"you can claim sites. You can do that here in under a minute:\n\n" + -auth.registrationUrl() + "\n\n"); + Console.error( + "You need to set a password on your Meteor developer account before", + "you can claim sites. You can do that here in under a minute:"); + Console.error(auth.registrationUrl()); + Console.error(); } else { - Console.stderr.write("Couldn't claim site: " + - result.errorMessage + "\n"); + Console.error("Couldn't claim site: " + result.errorMessage); } return 1; } - Console.stdout.write( -site + ": " + "successfully transferred to your account.\n" + -"\n" + -"Show authorized users with:\n" + -" meteor authorized " + site + "\n" + -"\n" + -"Add authorized users with:\n" + -" meteor authorized " + site + " --add \n" + -"\n" + -"Remove authorized users with:\n" + -" meteor authorized " + site + " --remove \n" + -"\n"); + Console.info(site + ": " + "successfully transferred to your account."); + Console.info(); + Console.info("Show authorized users with:"); + Console.info( + Console.command("meteor authorized " + site), + Console.options({ indent: 2 })); + Console.info(); + Console.info("Add authorized users with:"); + Console.info( + Console.command("meteor authorized " + site + " --add "), + Console.options({ indent: 2 })); + Console.info(); + Console.info("Remove authorized users with:"); + Console.info( + Console.command("meteor authorized " + site + " --remove "), + Console.options({ indent: 2 })); + Console.info(); return 0; }; @@ -748,19 +760,18 @@ var listSites = function () { }); if (result.errorMessage) { - Console.stderr.write("Couldn't list sites: " + - result.errorMessage + "\n"); + Console.error("Couldn't list sites: " + result.errorMessage); return 1; } if (! result.payload || ! result.payload.sites || ! result.payload.sites.length) { - Console.stdout.write("You don't have any sites yet.\n"); + Console.info("You don't have any sites yet."); } else { result.payload.sites.sort(); _.each(result.payload.sites, function (site) { - Console.stdout.write(site + "\n"); + Console.info(site); }); } return 0; diff --git a/tools/isopack.js b/tools/isopack.js index 813f5afc8a..4be10ffc96 100644 --- a/tools/isopack.js +++ b/tools/isopack.js @@ -14,6 +14,7 @@ var isopackets = require("./isopackets.js"); var isopackCacheModule = require('./isopack-cache.js'); var packageMapModule = require('./package-map.js'); var Future = require('fibers/future'); +var Console = require('./console.js').Console; var rejectBadPath = function (p) { if (p.match(/\.\./)) @@ -1090,8 +1091,8 @@ _.extend(Isopack.prototype, { // and similar to a isopacket load failure, it can just crash the app // instead of being handled nicely. if (messages.hasMessages()) { - process.stderr.write("Errors prevented tool build:\n"); - process.stderr.write(messages.formatMessages()); + Console.error("Errors prevented tool build:"); + Console.error(messages.formatMessages()); throw new Error("tool build failed?"); } diff --git a/tools/isopackets.js b/tools/isopackets.js index 1d24792573..b6160ee37f 100644 --- a/tools/isopackets.js +++ b/tools/isopackets.js @@ -218,7 +218,7 @@ var newIsopacketBuildingCatalog = function () { }); }); if (messages.hasMessages()) { - Console.error("=> Errors while scanning core packages:"); + Console.arrowError("Errors while scanning core packages:"); Console.printMessages(messages); throw new Error("isopacket scan failed?"); } diff --git a/tools/main.js b/tools/main.js index 1f332309f4..cfc6042be6 100644 --- a/tools/main.js +++ b/tools/main.js @@ -401,13 +401,13 @@ var springboard = function (rel, options) { // to! That's bad. Let's exit. if (options.fromApp) { Console.error( -"Sorry, this project uses " + rel.getDisplayName() + ", which is not\n" + -"installed and could not be downloaded. Please check to make sure that you\n" + -"are online."); + "Sorry, this project uses " + rel.getDisplayName() + ", which is not", + "installed and could not be downloaded. Please check to make sure", + "that you are online."); } else { Console.error( -"Sorry, " + rel.getDisplayName() + " is not installed and could not be\n" + -"downloaded. Please check to make sure that you are online."); + "Sorry, " + rel.getDisplayName() + " is not installed and could not", + "be downloaded. Please check to make sure that you are online."); } process.exit(1); } @@ -712,8 +712,8 @@ Fiber(function () { if (_.has(rawOptions, '--release')) { if (rawOptions['--release'].length > 1) { Console.error( -"--release should only be passed once.\n" + -"Try 'meteor help' for help."); + "--release should only be passed once. " + + "Try 'meteor help' for help."); process.exit(1); } releaseOverride = rawOptions['--release'][0]; @@ -721,8 +721,8 @@ Fiber(function () { releaseExplicit = true; if (! releaseOverride) { Console.error( -"The --release option needs a value.\n" + -"Try 'meteor help' for help."); + "The --release option needs a value. " + + "Try 'meteor help' for help."); process.exit(1); } delete rawOptions['--release']; @@ -748,19 +748,21 @@ Fiber(function () { // shouldn't happen unless the user did it manually. if (appReleaseFile.noReleaseSpecified()) { Console.error( -"Problem! This project has a .meteor/release file which is empty.\n" + -"The file should either contain the release of Meteor that you want to use,\n" + -"or the word 'none' if you will only use the project with unreleased\n" + -"checkouts of Meteor. Please edit the .meteor/release file in the project\n" + -"and change it to a valid Meteor release or 'none'."); + "Problem! This project has a .meteor/release file which is empty.", + "The file should either contain the release of Meteor that you want", + "to use, or the word 'none' if you will only use the project with", + "unreleased checkouts of Meteor. Please edit the .meteor/release", + "file in the project and change it to a valid Meteor release or", + "'none'."); process.exit(1); } else if (appReleaseFile.fileMissing()) { Console.error( -"Problem! This project does not have a .meteor/release file.\n" + -"The file should either contain the release of Meteor that you want to use,\n" + -"or the word 'none' if you will only use the project with unreleased\n" + -"checkouts of Meteor. Please edit the .meteor/release file in the project\n" + -"and change it to a valid Meteor release or 'none'."); + "Problem! This project does not have a .meteor/release file.", + "The file should either contain the release of Meteor that you", + "want to use, or the word 'none' if you will only use the project", + "with unreleased checkouts of Meteor. Please edit the", + ".meteor/release file in the project and change it to a valid Meteor", + "release or 'none'."); process.exit(1); } } @@ -805,11 +807,12 @@ Fiber(function () { if (!releaseName) { if (catalog.refreshFailed) { Console.error( -"The package catalog has no information about any Meteor releases, and we\n" + -"had trouble connecting to the package server."); + "The package catalog has no information about any Meteor", + "releases, and we had trouble connecting to the package server."); } else { Console.error( -"The package catalog has no information about any Meteor releases."); + "The package catalog has no information about", + "any Meteor releases."); } process.exit(1); } @@ -883,7 +886,8 @@ Fiber(function () { } else if (e instanceof files.OfflineError) { if (!catalog.refreshFailed) { // Warn if we didn't already warn. - Console.warn("Unable to contact release server (are you offline?)"); + Console.warn( + "Unable to contact release server (are you offline?)"); } // Treat this like a failure to refresh the catalog // (map the old world to the new world) @@ -918,17 +922,18 @@ Fiber(function () { } if (catalog.refreshFailed) { Console.error( -"This project says that it uses " + displayRelease + ", but\n" + -"you don't have that version of Meteor installed, and we were unable to\n" + -"contact Meteor's update servers to find out about it. Please edit the\n" + -".meteor/release file in the project and change it to a valid Meteor\n" + -"release, or go online."); + "This project says that it uses " + displayRelease + ", but", + "you don't have that version of Meteor installed, and we were", + "unable to contact Meteor's update servers to find out about it.", + "Please edit the .meteor/release file in the project and change", + "it to a valid Meteor release, or go online."); } else { Console.error( -"This project says that it uses " + displayRelease + ", but you don't have\n" + -"that version of Meteor installed and the Meteor update servers\n" + -"don't have it either. Please edit the .meteor/release file in\n" + -"the project and change it to a valid Meteor release."); + "This project says that it uses " + displayRelease + ", but you", + "don't have that version of Meteor installed and the Meteor", + "update servers don't have it either. Please edit the", + ".meteor/release file in the project and change it to a valid", + "Meteor release."); } } else { throw new Error("can't load latest release?"); @@ -971,12 +976,12 @@ Fiber(function () { if (rawOptions[fullName]) { if (rawOptions[fullName].length > 1) { Console.error("It doesn't make sense to pass " + - fullName + " more than once."); + fullName + " more than once."); process.exit(1); } if (_.size(rawOptions) > 1 || rawArgs.length !== 0 || command) { Console.error("Can't pass anything else along with " + - value.name + "."); + value.name + "."); process.exit(1); } command = value; @@ -1017,7 +1022,9 @@ Fiber(function () { if (! _.has(walk, word)) { Console.error( -"'" + commandName + "' is not a Meteor command. See 'meteor --help'."); + Console.command("'" + commandName + "'") + + " is not a Meteor command. See " + + Console.command("'meteor --help'")+ "."); process.exit(1); } @@ -1036,7 +1043,8 @@ Fiber(function () { // They typed something like 'meteor admin' (when they were // supposed to type 'meteor admin grant' or something). Console.error( -"Try 'meteor " + commandName + " help' for available commands."); + "Try " + Console.command("'meteor " + commandName + " help'") + " " + + "for available commands."); process.exit(1); } @@ -1048,7 +1056,9 @@ Fiber(function () { // which case showHelp will be true and command will be null if (showHelp) { - Console.stdout.write(longHelp(commandName) + "\n"); + // XXX: Until we rewrite the longHelp function to cope with the new output + // format, let's go with the static, painstakingly-formatted version. + Console.rawInfo(longHelp(commandName)); process.exit(0); } @@ -1061,13 +1071,16 @@ Fiber(function () { var presentLong = _.has(rawOptions, "--" + optionName); var presentShort = _.has(optionInfo, 'short') && _.has(rawOptions, "-" + optionInfo.short); + var tryHelpMessage = + "Try " + Console.command("'meteor help " + commandName + "'") + " " + + "for help."; + if (presentShort && presentLong) { // this would get caught below, but give a clearer error message Console.error( -commandName + ": can't pass both -" + optionInfo.short + " and --" + - optionName + ".\n" + -"Try 'meteor help " + commandName + "' for help."); + commandName + ": can't pass both -" + optionInfo.short + " and --" + + optionName + ". " + tryHelpMessage); process.exit(1); } var helpfulOptionName = "--" + optionName + @@ -1086,8 +1099,10 @@ commandName + ": can't pass both -" + optionInfo.short + " and --" + // in the future, we could support multiple values, but we don't // for now since no command needs it Console.error( -commandName + ": can only take one " + helpfulOptionName + " option.\n" + -"Try 'meteor help " + commandName + "' for help."); + Console.command(commandName) + ": can only take one " + + Console.command(helpfulOptionName) + " option."); + Console.error(tryHelpMessage); + process.exit(1); } else if (values.length === 1) { // OK, they provided exactly one value. Check its type and add @@ -1097,22 +1112,27 @@ commandName + ": can only take one " + helpfulOptionName + " option.\n" + // This option requires a value and they didn't give it one // (it was the last word on the command line). Console.error( -commandName + ": the " + helpfulOptionName + " option needs a value.\n" + -"Try 'meteor help " + commandName + "' for help."); + Console.command(commandName) + ": the " + + Console.command(helpfulOptionName) + " option needs a value."); + Console.error(tryHelpMessage); + process.exit(1); } else if (optionInfo.type === Number) { if (! value.match(/^[0-9]+$/)) { Console.error( -commandName + ": " + helpfulOptionName + " must be a number.\n" + -"Try 'meteor help " + commandName + "' for help."); + Console.command(commandName) + ": " + + Console.command(helpfulOptionName) + " must be a number."); + Console.error(tryHelpMessage); process.exit(1); } value = parseInt(value); } else if (optionInfo.type === Boolean) { if (!value) { Console.error( -commandName + ": the " + helpfulOptionName + " option does not need a value.\n" + -"Try 'meteor help " + commandName + "' for help."); + Console.command(commandName) + ": the " + + Console.command(helpfulOptionName) + " " + + "option does not need a value."); + Console.error(tryHelpMessage); process.exit(1); } value = true; @@ -1137,8 +1157,9 @@ commandName + ": the " + helpfulOptionName + " option does not need a value.\n" options[optionName] = optionInfo.default; } else if (optionInfo.required) { Console.error( -commandName + ": the --" + optionName + " option is required.\n" + -longHelp(commandName)); + Console.command(commandName) + ": the --" + + Console.command(optionName) + " option is required."); + Console.error(longHelp(commandName)); process.exit(1); } } @@ -1147,23 +1168,23 @@ longHelp(commandName)); // Check for unrecognized options. if (_.keys(rawOptions).length > 0) { Console.error( -_.keys(rawOptions)[0] + ": unknown option.\n" + -longHelp(commandName)); + Console.command(_.keys(rawOptions)[0]) + ": unknown option.\n" + + longHelp(commandName)); process.exit(1); } // Check argument count. if (options.args.length < command.minArgs) { Console.error( -commandName + ": not enough arguments.\n" + -longHelp(commandName)); + Console.command(commandName) + ": not enough arguments.\n" + + longHelp(commandName)); process.exit(1); } if (options.args.length > command.maxArgs) { Console.error( -commandName + ": too many arguments.\n" + -longHelp(commandName)); + Console.command(commandName) + ": too many arguments.\n" + + longHelp(commandName)); process.exit(1); } @@ -1183,14 +1204,20 @@ longHelp(commandName)); // since you'll default to the 'run' command which requires an // app. Be welcoming to our new developers! Console.error( -commandName + ": You're not in a Meteor project directory.\n" + -"\n" + -"To create a new Meteor project:\n" + -" meteor create \n" + -"For example:\n" + -" meteor create myapp\n" + -"\n" + -"For more help, see 'meteor --help'."); + Console.command(commandName) + + ": You're not in a Meteor project directory."); + Console.error(); + Console.error("To create a new Meteor project:"); + Console.error( + Console.command("meteor create "), + Console.options({ indent: 2 })); + Console.error("For example:"); + Console.error( + Console.command("meteor create myapp"), + Console.options({ indent: 2 })); + Console.error(); + Console.error( + "For more help, see " + Console.command("'meteor --help'") + "."); process.exit(1); } @@ -1211,18 +1238,20 @@ commandName + ": You're not in a Meteor project directory.\n" + if (! options.packageDir) { Console.error( - commandName + ": You're not in a Meteor package directory."); + Console.command(commandName) + + ": You're not in a Meteor package directory."); process.exit(1); } } if (command.requiresRelease && ! release.current) { Console.error( -"You must specify a Meteor version with --release when you work with this\n" + -"project. It was created from an unreleased Meteor checkout and doesn't\n" + -"have a version associated with it.\n" + -"\n" + -"You can permanently set a release for this project with 'meteor update'."); + "You must specify a Meteor version with --release when you work with", + "this project. It was created from an unreleased Meteor checkout and", + "doesn't have a version associated with it."); + Console.error( + "You can permanently set a release for this project with " + + Console.command("'meteor update'") + "."); process.exit(1); } @@ -1230,9 +1259,9 @@ commandName + ": You're not in a Meteor project directory.\n" + appReleaseFile && ! appReleaseFile.isCheckout()) { // For commands that work with apps, if we have overridden the // app's usual release by using a checkout, print a reminder banner. - Console.warn( -"=> Running Meteor from a checkout -- overrides project version (" + - appReleaseFile.displayReleaseName + ")"); + Console.arrowWarn( + "Running Meteor from a checkout -- overrides project version " + + Console.command("(" + appReleaseFile.displayReleaseName + ")")); } // Now that we're ready to start executing the command, if we are in @@ -1264,7 +1293,7 @@ commandName + ": You're not in a Meteor project directory.\n" + throw new Error( "you meant 'throw new main.Foo', not 'throw main.Foo'"); } else if (e instanceof main.ShowUsage) { - Console.error(longHelp(commandName)); + Console.rawError(longHelp(commandName)); process.exit(1); } else if (e instanceof main.SpringboardToLatestRelease) { // Load the metadata for the latest release (or at least, the latest diff --git a/tools/run-all.js b/tools/run-all.js index 3070917712..0f93697340 100644 --- a/tools/run-all.js +++ b/tools/run-all.js @@ -129,7 +129,7 @@ _.extend(Runner.prototype, { // print the banner only once we've successfully bound the port if (! self.quiet && ! self.stopped) { runLog.log("[[[[[ " + self.banner + " ]]]]]\n"); - runLog.log("=> Started proxy."); + runLog.log("Started proxy.", { arrow: true }); } self._startMongoAsync(); @@ -142,7 +142,7 @@ _.extend(Runner.prototype, { if (! self.stopped && self.httpProxy) { self.httpProxy.start(); if (! self.quiet) { - runLog.log("=> Started http proxy."); + runLog.log("Started http proxy.", { arrow: true }); } } @@ -153,7 +153,7 @@ _.extend(Runner.prototype, { extraRunner.start(); }); if (! self.quiet && ! self.stopped) - runLog.log("=> Started " + title + "."); + runLog.log("Started " + title + ".", { arrow: true }); } }); @@ -162,18 +162,20 @@ _.extend(Runner.prototype, { self.appRunner.start(); }); if (! self.quiet && ! self.stopped) - runLog.log("=> Started your app."); + runLog.log("Started your app.", { arrow: true }); } - if (! self.stopped && ! self.quiet) - runLog.log("\n=> App running at: " + self.rootUrl); + if (! self.stopped && ! self.quiet) { + runLog.log(""); + runLog.log("App running at: " + self.rootUrl, { arrow: true }); + } if (self.selenium && ! self.stopped) { buildmessage.enterJob({ title: "Starting Selenium" }, function () { self.selenium.start(); }); if (! self.quiet && ! self.stopped) - runLog.log("=> Started Selenium."); + runLog.log("Started Selenium.", { arrow: true }); } // XXX It'd be nice to (cosmetically) handle failure better. Right @@ -191,7 +193,7 @@ _.extend(Runner.prototype, { _startMongoFuture: function () { this.mongoRunner.start(); if (! this.stopped && ! this.quiet) { - runLog.log("=> Started MongoDB."); + runLog.log("Started MongoDB.", { arrow: true }); } }.future(), @@ -321,25 +323,26 @@ exports.run = function (options) { runner.stop(); if (result.outcome === "conflicting-versions") { - process.stderr.write( -"The constraint solver could not find a set of package versions to use that would\n" + -"satisfy the constraints of .meteor/versions and .meteor/packages. This could be\n" + -"caused by conflicts in .meteor/versions, conflicts in .meteor/packages, and/or\n" + -"inconsistent changes to the dependencies in local packages."); + Console.error( + "The constraint solver could not find a set of package versions to", + "use that would satisfy the constraints of .meteor/versions and", + ".meteor/packages. This could be caused by conflicts in", + ".meteor/versions, conflicts in .meteor/packages, and/or", + "inconsistent changes to the dependencies in local packages."); return 254; } if (result.outcome === "outdated-cordova-plugins") { - process.stderr.write( -"Your app's Cordova plugins have changed.\n" + -"Restart meteor to use the new set of plugins.\n"); + Console.error( + "Your app's Cordova plugins have changed.", + "Restart meteor to use the new set of plugins."); return 254; } if (result.outcome === "outdated-cordova-platforms") { - process.stderr.write( -"Your app's platforms have changed.\n" + -"Restart meteor to use the new set of platforms.\n"); + Console.error( + "Your app's platforms have changed.", + "Restart meteor to use the new set of platforms."); return 254; } @@ -357,10 +360,9 @@ exports.run = function (options) { // this (which prevents weird errors) is a start.) var from = release.current.getDisplayName(); var to = result.displayReleaseNeeded; - process.stderr.write( -"Your app has been updated to " + to + " from " + from + -".\n" + -"Restart meteor to use the new release.\n"); + Console.error( + "Your app has been updated to " + to + " from " + from + ".", + "Restart meteor to use the new release."); return 254; } @@ -373,14 +375,14 @@ exports.run = function (options) { } if (once && result.outcome === "bundle-fail") { - process.stderr.write("=> Build failed:\n\n" + - result.errors.formatMessages() + "\n"); + Console.arrowError("Build failed:\n\n" + + result.errors.formatMessages()); return 254; } if (once && result.outcome === "terminated") { if (result.signal) { - process.stderr.write("Killed (" + result.signal + ")\n"); + Console.error("Killed (" + result.signal + ")"); return 255; } else if (typeof result.code === "number") { // We used to print 'Your application is exiting' here, but that diff --git a/tools/run-app.js b/tools/run-app.js index e101315561..c5de4f2122 100644 --- a/tools/run-app.js +++ b/tools/run-app.js @@ -114,7 +114,7 @@ _.extend(AppProcess.prototype, { })); self.proc.on('error', fiberHelpers.inBareFiber(function (err) { - runLog.log("=> Couldn't spawn process: " + err.message); + runLog.log("Couldn't spawn process: " + err.message, { arrow: true }); // node docs say that it might make both an 'error' and a // 'close' callback, so we use a guard to make sure we only call @@ -751,11 +751,11 @@ _.extend(AppRunner.prototype, { } else if (runResult.outcome === "bundle-fail") { - runLog.log("=> Errors prevented startup:\n\n" + - runResult.errors.formatMessages()); + runLog.log("Errors prevented startup:\n\n" + + runResult.errors.formatMessages(), { arrow: true }); if (self.watchForChanges) { - runLog.log("=> Your application has errors. " + - "Waiting for file change."); + runLog.log("Your application has errors. " + + "Waiting for file change.", { arrow: true }); Console.enableProgressDisplay(false); } } @@ -765,9 +765,9 @@ _.extend(AppRunner.prototype, { else if (runResult.outcome === "terminated") { if (runResult.signal) { - runLog.log('=> Exited from signal: ' + runResult.signal); + runLog.log('Exited from signal: ' + runResult.signal, { arrow: true }); } else if (runResult.code !== undefined) { - runLog.log('=> Exited with code: ' + runResult.code); + runLog.log('Exited with code: ' + runResult.code, { arrow: true }); } else { // explanation should already have been logged } @@ -777,8 +777,9 @@ _.extend(AppRunner.prototype, { continue; if (self.watchForChanges) { - runLog.log("=> Your application is crashing. " + - "Waiting for file change."); + runLog.log("Your application is crashing. " + + "Waiting for file change.", + { arrow: true }); Console.enableProgressDisplay(false); } } @@ -805,7 +806,7 @@ _.extend(AppRunner.prototype, { // While we were waiting, did somebody stop() us? if (self.exitFuture) break; - runLog.log("=> Modified -- restarting."); + runLog.log("Modified -- restarting.", { arrow: true }); Console.enableProgressDisplay(true); continue; } diff --git a/tools/run-log.js b/tools/run-log.js index edd4ca5d72..0a308b7c6c 100644 --- a/tools/run-log.js +++ b/tools/run-log.js @@ -100,9 +100,13 @@ _.extend(RunLog.prototype, { // XXX deal with test server logging differently?! }, - log: function (msg) { + // Log the message. + // msg: message + // options: + // - arrow: if true, preface with => and wrap accordingly. + log: function (msg, options) { var self = this; - + options = options || {}; var obj = { time: new Date, message: msg @@ -113,7 +117,19 @@ _.extend(RunLog.prototype, { self._record(obj); self._clearSpecial(); - Console.stdout.write(msg + "\n"); + + // Process the options. By default, we want to wordwrap the message with + // Console.info. If we ask for raw output, then we don't want to do that. If + // we ask for an arrow, we want to wrap around with => as the bulletPoint. + var printFn; + if (options.arrow) { + printFn = Console.arrowInfo; + } else { + printFn = Console.info; + } + + // Print the message to the logs. + printFn(msg); }, // Write a message to the terminal that will get overwritten by the diff --git a/tools/run-velocity.js b/tools/run-velocity.js index ee30700ee8..e7a9ba489e 100644 --- a/tools/run-velocity.js +++ b/tools/run-velocity.js @@ -30,8 +30,8 @@ var runVelocity = function (url) { ddpConnection.subscribe("VelocityTestReports", { onError: function () { - Console.stderr.write("failed to subscribe to VelocityTestReports " - + "subscription"); + Console.error("failed to subscribe to VelocityTestReports " + + "subscription"); // XXX tell user to add velocity:core // XXX these also fire if the user turns on autopublish }, onReady: function () { @@ -65,8 +65,8 @@ var runVelocity = function (url) { var isFinished = false; ddpConnection.subscribe("VelocityAggregateReports", { onError: function () { - Console.stderr.write("failed to subscribe to " + - "VelocityAggregateReports subscription"); + Console.error("failed to subscribe to " + + "VelocityAggregateReports subscription"); }, onReady: function () { this.connection.registerStore("velocityAggregateReports", { update: function (msg) { @@ -113,8 +113,8 @@ var runVelocity = function (url) { ddpConnection.subscribe("VelocityMirrors", { onError: function (err) { - Console.stderr.write("failed to subscribe to VelocityMirrors " + - "subscription", err); + Console.error("failed to subscribe to VelocityMirrors " + + "subscription", err); }, onReady: function () { this.connection.registerStore("velocityMirrors", { update: function (msg) { diff --git a/tools/selftest.js b/tools/selftest.js index 33718c4c10..ee6548dbe1 100644 --- a/tools/selftest.js +++ b/tools/selftest.js @@ -171,7 +171,7 @@ var newSelfTestCatalog = function () { }); }); if (messages.hasMessages()) { - Console.error("=> Errors while scanning core packages:"); + Console.arrowError("Errors while scanning core packages:"); Console.printMessages(messages); throw new Error("scan failed?"); } @@ -240,7 +240,7 @@ _.extend(Matcher.prototype, { var self = this; if (self.buf.length > 0) { - console.log("Extra junk is ", self.buf); + Console.info("Extra junk is :", self.buf); throw new TestFailure('junk-at-end', { run: self.run }); } }, @@ -1639,7 +1639,7 @@ var listTests = function (options) { var testList = getFilteredTests(options); if (! testList.allTests.length) { - Console.stderr.write("No tests defined.\n"); + Console.error("No tests defined.\n"); return; } @@ -1652,9 +1652,9 @@ var listTests = function (options) { }); }); - Console.stderr.write('\n'); - Console.stderr.write(testList.filteredTests.length + " tests listed."); - Console.stderr.write(testList.generateSkipReport()); + Console.error(); + Console.error(testList.filteredTests.length + " tests listed."); + Console.error(testList.generateSkipReport()); }; /////////////////////////////////////////////////////////////////////////////// @@ -1669,7 +1669,7 @@ var runTests = function (options) { var testList = getFilteredTests(options); if (! testList.allTests.length) { - Console.stderr.write("No tests defined.\n"); + Console.error("No tests defined."); return 0; } @@ -1689,7 +1689,7 @@ var runTests = function (options) { if (e instanceof TestFailure) { failure = e; } else { - Console.stderr.write("exception\n\n"); + Console.error("exception\n"); throw e; } } finally { @@ -1698,84 +1698,85 @@ var runTests = function (options) { } if (failure) { - Console.stderr.write("fail!\n"); + Console.error("fail!"); failedTests.push(test); testList.notifyFailed(test); var frames = parseStack.parse(failure); var relpath = path.relative(files.getCurrentToolsDir(), frames[0].file); - Console.stderr.write(" => " + failure.reason + " at " + - relpath + ":" + frames[0].line + "\n"); + Console.rawError(" => " + failure.reason + " at " + + relpath + ":" + frames[0].line); if (failure.reason === 'no-match') { - Console.stderr.write(" => Pattern: " + failure.details.pattern + "\n"); + Console.arrowError("Pattern: " + failure.details.pattern, 2); } if (failure.reason === "wrong-exit-code") { var s = function (status) { return status.signal || ('' + status.code) || "???"; }; - Console.stderr.write(" => Expected: " + s(failure.details.expected) + - "; actual: " + s(failure.details.actual) + "\n"); + Console.rawError(" => " + "Expected: " + s(failure.details.expected) + + "; actual: " + s(failure.details.actual)); } if (failure.reason === 'expected-exception') { } if (failure.reason === 'not-equal') { - Console.stderr.write( - " => Expected: " + JSON.stringify(failure.details.expected) + - "; actual: " + JSON.stringify(failure.details.actual) + "\n"); + Console.rawError( + " => " + "Expected: " + JSON.stringify(failure.details.expected) + + "; actual: " + JSON.stringify(failure.details.actual)); } if (failure.details.run) { failure.details.run.outputLog.end(); var lines = failure.details.run.outputLog.get(); if (! lines.length) { - Console.stderr.write(" => No output\n"); + Console.arrowError("No output", 2); } else { var historyLines = options.historyLines || 100; - Console.stderr.write(" => Last " + historyLines + " lines:\n"); + Console.arrowError("Last " + historyLines + " lines:", 2 + ); _.each(lines.slice(-historyLines), function (line) { - Console.stderr.write(" " + - (line.channel === "stderr" ? "2| " : "1| ") + - line.text + - (line.bare ? "%" : "") + "\n"); + Console.rawError(" " + + (line.channel === "stderr" ? "2| " : "1| ") + + line.text + + (line.bare ? "%" : "")); }); } } if (failure.details.messages) { - Console.stderr.write(" => Errors while building:\n"); - Console.stderr.write(failure.details.messages.formatMessages()); + Console.arrowError("Errors while building:", 2); + Console.rawError(failure.details.messages.formatMessages()); } } else { var durationMs = +(new Date) - startTime; - Console.stderr.write("ok (" + durationMs + " ms)\n"); + Console.error("ok (" + durationMs + " ms)"); } }); testList.saveTestState(); if (totalRun > 0) - Console.stderr.write("\n"); + Console.error(); - Console.stderr.write(testList.generateSkipReport()); + Console.error(testList.generateSkipReport()); if (testList.filteredTests.length === 0) { - Console.stderr.write("No tests run.\n"); + Console.error("No tests run."); return 0; } else if (failedTests.length === 0) { var disclaimers = ''; if (testList.filteredTests.length < testList.allTests.length) disclaimers += " other"; - Console.stderr.write("All" + disclaimers + " tests passed.\n"); + Console.error("All" + disclaimers + " tests passed."); return 0; } else { var failureCount = failedTests.length; - Console.stderr.write(failureCount + " failure" + - (failureCount > 1 ? "s" : "") + ":\n"); + Console.error(failureCount + " failure" + + (failureCount > 1 ? "s" : "") + ":"); _.each(failedTests, function (test) { - Console.stderr.write(" - " + test.file + ": " + test.name); + Console.rawError(" - " + test.file + ": " + test.name); }); return 1; } diff --git a/tools/stats.js b/tools/stats.js index 0ece142866..588c6a400a 100644 --- a/tools/stats.js +++ b/tools/stats.js @@ -118,11 +118,13 @@ var recordPackages = function (options) { var logErrorIfInCheckout = function (err) { if (files.inCheckout() || process.env.METEOR_PACKAGE_STATS_TEST_OUTPUT) { - Console.stderr.write("Failed to record package usage.\n"); - Console.stderr.write( - "(This error is hidden when you are not running Meteor from a checkout.)\n"); - Console.stderr.write(err.stack || err); - Console.stderr.write("\n\n"); + Console.warn("Failed to record package usage."); + Console.warn( + "(This error is hidden when you are not running Meteor from a", + "checkout.)"); + Console.rawWarn(err.stack || err); + Console.warn(); + Console.warn(); } }; diff --git a/tools/tests/package-tests.js b/tools/tests/package-tests.js index 2ffd21c9f1..ad2aac35f8 100644 --- a/tools/tests/package-tests.js +++ b/tools/tests/package-tests.js @@ -872,7 +872,7 @@ selftest.define("add package with no builds", ["net"], function () { var run = s.run("add", "glasser:binary-package-with-no-builds"); run.waitSecs(10); - run.matchErr("No compatible build found for " + + run.matchErr("No compatible build found for\n" + "glasser:binary-package-with-no-builds@1.0.0"); run.expectExit(1); }); diff --git a/tools/tests/releases.js b/tools/tests/releases.js index 5460578be2..18c9edaedd 100644 --- a/tools/tests/releases.js +++ b/tools/tests/releases.js @@ -79,7 +79,8 @@ selftest.define("springboard", ['checkout', 'net'], function () { run = s.run(); run.matchErr("offline"); run.matchErr("it uses Meteor strange"); - run.matchErr("don't have that version of Meteor installed"); + run.matchErr("don't have that version"); + run.matchErr("of Meteor installed"); run.matchErr("update servers"); run.expectExit(1); @@ -186,7 +187,8 @@ selftest.define("checkout", ['checkout'], function () { s.write(".meteor/release", "something"); run = s.run("list"); run.readErr("=> Running Meteor from a checkout"); - run.matchErr("project version (Meteor something)\n"); + run.matchErr("project version"); + run.matchErr("(Meteor something)\n"); run.expectExit(0); }); }); diff --git a/tools/utils.js b/tools/utils.js index 02d9ebc5b0..b0966966ea 100644 --- a/tools/utils.js +++ b/tools/utils.js @@ -112,49 +112,8 @@ exports.printPackageList = function (items, options) { }; rows = _.sortBy(rows, alphaSort); - return utils.printTwoColumns(rows, options); -}; - -// XXX: Move to e.g. formatters.js? -// Prints a two column table in a nice format: -// The first column is printed entirely, the second only as space permits -exports.printTwoColumns = function (rows, options) { - options = options || {}; - - var longest = ''; - _.each(rows, function (row) { - var col0 = row[0] || ''; - if (col0.length > longest.length) - longest = col0; - }); - - var pad = longest.replace(/./g, ' '); - - var width = 80; - var stream = process.stdout; - if (stream && stream.isTTY && stream.columns) { - width = stream.columns; - } - - var Console = require("./console.js").Console; - - var out = ''; - _.each(rows, function (row) { - var col0 = row[0] || ''; - var col1 = row[1] || ''; - var line = Console.bold(col0) + pad.substr(col0.length); - line += " " + col1; - if (line.length > width) { - line = line.substr(0, width - 3) + '...'; - } - out += line + "\n"; - }); - - // XXX: Naughty call to 'private' function - var level = options.level || Console.LEVEL_INFO; - Console._print(level, out); - - return out; + var Console = require('./console.js').Console; + return Console.printTwoColumns(rows, options); }; // Determine a human-readable hostname for this computer. Prefer names @@ -300,7 +259,8 @@ exports.validatePackageNameOrExit = function (packageName, options) { } catch (e) { if (!e.versionParserError) throw e; - process.stderr.write("Error: " + e.message + "\n"); + var Console = require('./console.js').Console; + Console.error(e.message, Console.options({ bulletPoint: "Error: " })); // lazy-load main: old bundler tests fail if you add a circular require to // this file var main = require('./main.js'); From dd96477c51336b3971f65ae04fed122adb6428b1 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 17:22:04 -0800 Subject: [PATCH 02/16] do not automatically print a newline Raw print functions should not actually print a new line -- that's an example of processing that we should be doing in the non-raw functions. As such, _print should not add a new line at the end! This means that other functions that DID intend to end a new line should add one manually. Also, a minor refactor to have less repetitive code in the wrapping functions. --- tools/console.js | 60 +++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/tools/console.js b/tools/console.js index de734921a7..561eb4eab0 100644 --- a/tools/console.js +++ b/tools/console.js @@ -489,10 +489,10 @@ var Console = function (options) { self.stdout = {}; self.stderr = {}; self.stdout.write = function (msg) { - self._legacyWrite(LEVEL_INFO, msg); + self._print(LEVEL_INFO, msg); }; self.stderr.write = function (msg) { - self._legacyWrite(LEVEL_WARN, msg); + self._print(LEVEL_WARN, msg); }; self._stream = process.stdout; @@ -514,7 +514,6 @@ var Console = function (options) { }); }; - var LEVEL_CODE_ERROR = 4; var LEVEL_CODE_WARN = 3; var LEVEL_CODE_INFO = 2; @@ -685,14 +684,8 @@ _.extend(Console.prototype, { var self = this; if (! self.isDebugEnabled()) { return; } - var parsedArgs = self._parseVariadicInput(arguments); - var wrapOpts = { - indent: parsedArgs.opts.indent, - bulletPoint: parsedArgs.opts.bulletPoint - }; - - var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); - self._print(LEVEL_DEBUG, wrappedMessage); + var message = self._prettifyMessage(arguments); + self._print(LEVEL_DEBUG, message); }, isInfoEnabled: function () { @@ -718,14 +711,9 @@ _.extend(Console.prototype, { var self = this; if (! self.isInfoEnabled()) { return; } - var parsedArgs = self._parseVariadicInput(arguments); - var wrapOpts = { - indent: parsedArgs.opts.indent, - bulletPoint: parsedArgs.opts.bulletPoint - }; - var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); - self._print(LEVEL_INFO, wrappedMessage); + var message = self._prettifyMessage(arguments); + self._print(LEVEL_INFO, message); }, isWarnEnabled: function () { @@ -745,18 +733,12 @@ _.extend(Console.prototype, { // Generally, we want to process the output for legibility, for example, by // wrapping it. For raw output (ex: stack traces, user logs, etc), use the // rawWarn function. For more information about options, see: debug. - warn: function(/*arguments*/) { + warn: function(/* arguments */) { var self = this; if (! self.isWarnEnabled()) { return; } - var parsedArgs = self._parseVariadicInput(arguments); - var wrapOpts = { - indent: parsedArgs.opts.indent, - bulletPoint: parsedArgs.opts.bulletPoint - }; - - var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); - self._print(LEVEL_WARN, wrappedMessage); + var message = self._prettifyMessage(arguments); + self._print(LEVEL_WARN, message); }, rawError: function(/*arguments*/) { @@ -768,10 +750,16 @@ _.extend(Console.prototype, { // Generally, we want to process the output for legibility, for example, by // wrapping it. For raw output (ex: stack traces, user logs, etc), use the - // rawWarn function. For more information about options, see: debug. + // rawError function. For more information about options, see: debug. error: function(/*arguments*/) { var self = this; + var message = self._prettifyMessage(arguments); + self._print(LEVEL_ERROR, message); + }, + + _prettifyMessage: function (/* arguments */) { + var self = this; var parsedArgs = self._parseVariadicInput(arguments); var wrapOpts = { indent: parsedArgs.opts.indent, @@ -779,15 +767,8 @@ _.extend(Console.prototype, { }; var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); - self._print(LEVEL_ERROR, wrappedMessage); - }, - - _legacyWrite: function (level, message) { - var self = this; - if(message.substr && message.substr(-1) == '\n') { - message = message.substr(0, message.length - 1); - } - self._print(level, message); + wrappedMessage += "\n"; + return wrappedMessage; }, _print: function(level, message) { @@ -825,9 +806,9 @@ _.extend(Console.prototype, { } if (style) { - dest.write(style(message + '\n')); + dest.write(style(message)); } else { - dest.write(message + '\n'); + dest.write(message); } // XXX: Pause before showing the progress display, to prevent @@ -1011,6 +992,7 @@ _.extend(Console.prototype, { }); var level = options.level || self.LEVEL_INFO; + out += "/n"; self._print(level, out); return out; From 25dfd39ef13c125e3e4082ea01f4144bd223e473 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 17:38:41 -0800 Subject: [PATCH 03/16] Everywhere that we call a raw function, add a new line --- tools/commands-cordova.js | 28 +++++++++++++++------------- tools/commands-packages.js | 3 ++- tools/commands.js | 10 +++++----- tools/console.js | 6 +++--- tools/main.js | 4 ++-- tools/selftest.js | 12 ++++++------ tools/stats.js | 3 ++- 7 files changed, 35 insertions(+), 31 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index ed091dcd61..fb18384d91 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -184,7 +184,7 @@ var setVerboseness = cordova.setVerboseness = function (v) { }; var verboseLog = cordova.verboseLog = function (/* args */) { if (verboseness) - Console.rawError('%% ' + util.format.apply(null, arguments)); + Console.rawError('%% ' + util.format.apply(null, arguments) + "\n"); }; @@ -374,8 +374,8 @@ var ensureCordovaProject = function (projectContext, appName) { if (err instanceof main.ExitWithCode) { process.exit(err.code); } - Console.rawError("Error creating Cordova project: " + - err.message + "\n" + err.stack); + Console.error("Error creating Cordova prject: " + err.message); + Console.rawError(err.stack + "\n"); } } }; @@ -2075,8 +2075,8 @@ _.extend(Android.prototype, { if (execution.exitCode !== 0) { Console.warn( "Unexpected exit code from android process: " + execution.exitCode); - Console.rawWarn("stdout: " + execution.stdout); - Console.rawWarn("stderr: " + execution.stderr); + Console.rawWarn("stdout: " + execution.stdout + "\n"); + Console.rawWarn("stderr: " + execution.stderr + "\n"); throw new Error("Error running android tool: exit code " + execution.exitCode); } @@ -2195,9 +2195,9 @@ _.extend(Android.prototype, { if (execution.exitCode !== 0) { Console.debug("Unable to run aapt." + " (This is normal if 32 bit libraries are not found)"); - Console.rawDebug(" exit code: " + execution.exitCode); - Console.rawDebug(" stdout: " + execution.stdout); - Console.rawDebug(" stderr: " + execution.stderr); + Console.rawDebug(" exit code: " + execution.exitCode + "\n"); + Console.rawDebug(" stdout: " + execution.stdout + "\n"); + Console.rawDebug(" stderr: " + execution.stderr + "\n"); return false; } @@ -2396,7 +2396,9 @@ _.extend(Android.prototype, { if (Host.hasAptGet()) { Console.info("You can install the JDK using:"); - Console.rawInfo(" sudo apt-get install --yes openjdk-7-jdk"); + Console.info( + Console.comand("sudo apt-get install --yes openjdk-7-jdk"), + Console.options({ indent: 2 })); // XXX: Technically, these are for Android, not installing Java if (processor == "x86_64") { @@ -2496,8 +2498,8 @@ _.extend(Android.prototype, { if (execution.exitCode != 0) { Console.warn( "Unexpected exit code from script: " + execution.exitCode); - Console.rawWarn("stdout: " + execution.stdout); - Console.rawWarn("stderr: " + execution.stderr); + Console.rawWarn("stdout: " + execution.stdout + "\n"); + Console.rawWarn("stderr: " + execution.stderr + "\n"); throw new Error('Could not download Android bundle'); } }); @@ -2592,7 +2594,7 @@ _.extend(Android.prototype, { device[kv[0]] = kv[1]; } devices.push(device); - Console.rawDebug("Found device", JSON.stringify(device)); + Console.rawDebug("Found device", JSON.stringify(device) + "\n"); }); return devices; }, @@ -2977,7 +2979,7 @@ main.registerCommand({ var platforms = projectContext.platformList.getPlatforms(); - Console.rawInfo(platforms.join("\n")); + Console.rawInfo(platforms.join("\n") + "\n"); }); main.registerCommand({ diff --git a/tools/commands-packages.js b/tools/commands-packages.js index 8bd197a71a..694b50f778 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -1174,7 +1174,8 @@ main.registerCommand({ var myMaintainerString = ""; var myMaintainers = _.pluck(record.maintainers, 'username'); if (myMaintainers.length === 0) { - Console.rawDebug("No maintainer records found: ", JSON.stringify(record)); + Console.rawDebug( + "No maintainer records found: ", JSON.stringify(record), "\n"); } else if (myMaintainers.length === 1) { myMaintainerString = myMaintainers[0]; } else { diff --git a/tools/commands.js b/tools/commands.js index 7dc6b6626d..0ddf9a4821 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -198,7 +198,7 @@ function doRunCommand (options) { } catch (err) { if (options.verbose) { Console.rawError( - "Error while parsing --port option: " + err.stack); + "Error while parsing --port option: " + err.stack + "\n"); } else { Console.error(err.message); } @@ -215,7 +215,7 @@ function doRunCommand (options) { } catch (err) { if (options.verbose) { Console.rawError( - "Error while parsing --mobile-server option: " + err.stack); + "Error while parsing --mobile-server option: " + err.stack + "\n"); } else { Console.error(err.message); } @@ -1676,7 +1676,7 @@ main.registerCommand({ if (body.organizations.length === 0) { Console.info("You are not a member of any organizations."); } else { - Console.rawInfo(_.pluck(body.organizations, "name").join("\n")); + Console.rawInfo(_.pluck(body.organizations, "name").join("\n") + "\n"); } return 0; }); @@ -1732,7 +1732,7 @@ main.registerCommand({ } var members = _.pluck(result, "username"); - Console.rawInfo(members.join("\n")); + Console.rawInfo(members.join("\n") + "\n"); } return 0; @@ -1925,7 +1925,7 @@ main.registerCommand({ 'key' : ret.sshKey, 'hostKey' : ret.hostKey }; - Console.rawInfo(JSON.stringify(retJson, null, 2)); + Console.rawInfo(JSON.stringify(retJson, null, 2) + "\n"); return 0; } diff --git a/tools/console.js b/tools/console.js index 561eb4eab0..b360f6e8fc 100644 --- a/tools/console.js +++ b/tools/console.js @@ -758,9 +758,9 @@ _.extend(Console.prototype, { self._print(LEVEL_ERROR, message); }, - _prettifyMessage: function (/* arguments */) { + _prettifyMessage: function (msgArguments) { var self = this; - var parsedArgs = self._parseVariadicInput(arguments); + var parsedArgs = self._parseVariadicInput(msgArguments); var wrapOpts = { indent: parsedArgs.opts.indent, bulletPoint: parsedArgs.opts.bulletPoint @@ -909,7 +909,7 @@ _.extend(Console.prototype, { self.error(message); if (self.verbose && err.stack) { - self.rawInfo(err.stack); + self.rawInfo(err.stack + "\n"); } }, diff --git a/tools/main.js b/tools/main.js index cfc6042be6..01ec8594fc 100644 --- a/tools/main.js +++ b/tools/main.js @@ -1058,7 +1058,7 @@ Fiber(function () { if (showHelp) { // XXX: Until we rewrite the longHelp function to cope with the new output // format, let's go with the static, painstakingly-formatted version. - Console.rawInfo(longHelp(commandName)); + Console.rawInfo(longHelp(commandName) + "\n"); process.exit(0); } @@ -1293,7 +1293,7 @@ Fiber(function () { throw new Error( "you meant 'throw new main.Foo', not 'throw main.Foo'"); } else if (e instanceof main.ShowUsage) { - Console.rawError(longHelp(commandName)); + Console.rawError(longHelp(commandName) + "\n"); process.exit(1); } else if (e instanceof main.SpringboardToLatestRelease) { // Load the metadata for the latest release (or at least, the latest diff --git a/tools/selftest.js b/tools/selftest.js index ee6548dbe1..ee0f45e97d 100644 --- a/tools/selftest.js +++ b/tools/selftest.js @@ -1706,7 +1706,7 @@ var runTests = function (options) { var relpath = path.relative(files.getCurrentToolsDir(), frames[0].file); Console.rawError(" => " + failure.reason + " at " + - relpath + ":" + frames[0].line); + relpath + ":" + frames[0].line + "\n"); if (failure.reason === 'no-match') { Console.arrowError("Pattern: " + failure.details.pattern, 2); } @@ -1716,14 +1716,14 @@ var runTests = function (options) { }; Console.rawError(" => " + "Expected: " + s(failure.details.expected) + - "; actual: " + s(failure.details.actual)); + "; actual: " + s(failure.details.actual) + "\n"); } if (failure.reason === 'expected-exception') { } if (failure.reason === 'not-equal') { Console.rawError( " => " + "Expected: " + JSON.stringify(failure.details.expected) + - "; actual: " + JSON.stringify(failure.details.actual)); + "; actual: " + JSON.stringify(failure.details.actual) + "\n"); } if (failure.details.run) { @@ -1740,14 +1740,14 @@ var runTests = function (options) { Console.rawError(" " + (line.channel === "stderr" ? "2| " : "1| ") + line.text + - (line.bare ? "%" : "")); + (line.bare ? "%" : "") + "\n"); }); } } if (failure.details.messages) { Console.arrowError("Errors while building:", 2); - Console.rawError(failure.details.messages.formatMessages()); + Console.rawError(failure.details.messages.formatMessages() + "\n"); } } else { var durationMs = +(new Date) - startTime; @@ -1776,7 +1776,7 @@ var runTests = function (options) { Console.error(failureCount + " failure" + (failureCount > 1 ? "s" : "") + ":"); _.each(failedTests, function (test) { - Console.rawError(" - " + test.file + ": " + test.name); + Console.rawError(" - " + test.file + ": " + test.name + "\n"); }); return 1; } diff --git a/tools/stats.js b/tools/stats.js index 588c6a400a..95fc3780a3 100644 --- a/tools/stats.js +++ b/tools/stats.js @@ -122,7 +122,8 @@ var logErrorIfInCheckout = function (err) { Console.warn( "(This error is hidden when you are not running Meteor from a", "checkout.)"); - Console.rawWarn(err.stack || err); + var printErr = err.stack || err; + Console.rawWarn(printErr + "\n"); Console.warn(); Console.warn(); } From 8bf5db2de081c2a2f36c232df50b9c088ac06436 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 17:42:07 -0800 Subject: [PATCH 04/16] eliminate legacy stdout.write and stderr.write functions from Console Now that we no longer have an automatic newline on printing, we don't have to support the awkward legacy functions that the Console used to provide. Eliminating. --- tools/auth.js | 4 ++-- tools/commands-cordova.js | 2 +- tools/console.js | 2 -- tools/processes.js | 5 ++--- tools/run-log.js | 6 +++--- tools/selftest.js | 8 ++++---- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/tools/auth.js b/tools/auth.js index 249c6e8ffe..8735b0ecce 100644 --- a/tools/auth.js +++ b/tools/auth.js @@ -936,8 +936,8 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { animationFrame = (animationFrame + 1) % spinner.length; }, 200); var stopSpinner = function () { - Console.stderr.write(new Array(lastLinePrinted.length + 1).join(' ') + - "\r"); + Console.rawError(new Array(lastLinePrinted.length + 1).join(' ') + + "\r"); clearInterval(timer); }; diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index fb18384d91..7a8f635eff 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1094,7 +1094,7 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { Console.error(); } else if (err) { Console.error(); - Console.stderr.write( + Console.error( chalk.green("Could not start your app."), chalk.green("Try running again with the --verbose option.") ); diff --git a/tools/console.js b/tools/console.js index b360f6e8fc..63d1644878 100644 --- a/tools/console.js +++ b/tools/console.js @@ -9,8 +9,6 @@ /// to pre-process the output.) /// - Progress bar support /// Display a progress bar on the screen, but hide it around log messages. -/// - 'legacy' functions: Console.stdout.write & Console.stderr.write -/// Make porting code a lot easier (just a regex from process -> Console) /// /// In future, we might do things like move all support for verbose mode in /// here, and also integrate the buildmessage functionality into here diff --git a/tools/processes.js b/tools/processes.js index 8df08c5d58..0dfb976a53 100644 --- a/tools/processes.js +++ b/tools/processes.js @@ -69,7 +69,7 @@ _.extend(RunCommand.prototype, { self.process.stdout.on('data', function (data) { self.stdout = self.stdout + data; if (self.options.pipeOutput) { - Console.stdout.write(data); + Console.rawInfo(data); } if (self.options.onStdout) { self.options.onStdout(data); @@ -79,7 +79,7 @@ _.extend(RunCommand.prototype, { self.process.stderr.on('data', function (data) { self.stderr = self.stderr + data; if (self.options.pipeOutput) { - Console.stderr.write(data); + Console.rawError(data); } if (self.options.onStderr) { self.options.onStderr(data); @@ -121,4 +121,3 @@ _.extend(RunCommand.prototype, { }); exports.RunCommand = RunCommand; - diff --git a/tools/run-log.js b/tools/run-log.js index 0a308b7c6c..a87af15606 100644 --- a/tools/run-log.js +++ b/tools/run-log.js @@ -62,12 +62,12 @@ _.extend(RunLog.prototype, { if (self.consecutiveRestartMessages) { self.consecutiveRestartMessages = null; - Console.stdout.write("\n"); + Console.info(); } if (self.consecutiveClientRestartMessages) { self.consecutiveClientRestartMessages = null; - Console.stdout.write("\n"); + Console.info(); } if (self.temporaryMessageLength) { @@ -95,7 +95,7 @@ _.extend(RunLog.prototype, { if (self.rawLogs) Console[isStderr ? "stderr" : "stdout"].write(line + "\n"); else - Console.stdout.write(Log.format(obj, { color: true }) + "\n"); + Console.rawInfo(Log.format(obj, { color: true }) + "\n"); // XXX deal with test server logging differently?! }, diff --git a/tools/selftest.js b/tools/selftest.js index ee0f45e97d..f0969a6ef6 100644 --- a/tools/selftest.js +++ b/tools/selftest.js @@ -1644,11 +1644,11 @@ var listTests = function (options) { } _.each(_.groupBy(testList.filteredTests, 'file'), function (tests, file) { - Console.stdout.write(file + ':\n'); + Console.rawInfo(file + ':\n'); _.each(tests, function (test) { - Console.stdout.write(' - ' + test.name + - (test.tags.length ? ' [' + test.tags.join(' ') + ']' - : '')); + Console.rawInfo(' - ' + test.name + + (test.tags.length ? ' [' + test.tags.join(' ') + ']' + : '')); }); }); From c9928d9c3a51ac855b5a4877085536801f773053 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 18:32:40 -0800 Subject: [PATCH 05/16] responding to minor fixes on 3232 - Wrapping URLs in Console.url. Making Console.url not word-wrap URLs, on the off-chance that a URL could be wrapped. - Creating a doNotWrap function on the Console. Call this on things that are not commands or URLs, but still should not be wrapped. - fixing minor mistypes, removing a comment about the dev bundle on the Windows branch. --- meteor | 2 +- tools/auth.js | 6 ++-- tools/commands-cordova.js | 22 ++++++------ tools/commands-packages.js | 33 ++++++++--------- tools/commands.js | 19 +++++----- tools/console.js | 72 ++++++++++++++++++++------------------ tools/deploy.js | 4 +-- tools/main.js | 13 ++++--- tools/run-all.js | 4 +-- tools/run-log.js | 10 +----- 10 files changed, 90 insertions(+), 95 deletions(-) diff --git a/meteor b/meteor index 5b2f7ed21d..6e5eb77379 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/bin/bash -BUNDLE_VERSION=0.3.74 # 0.3.63 on the Windows branch +BUNDLE_VERSION=0.3.74 # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. diff --git a/tools/auth.js b/tools/auth.js index 8735b0ecce..62c8061817 100644 --- a/tools/auth.js +++ b/tools/auth.js @@ -853,7 +853,7 @@ exports.whoAmICommand = function (options) { var username = currentUsername(data); if (username) { - Console.info(Console.command(username)); + Console.rawInfo(username + "\n"); return 0; } @@ -861,7 +861,7 @@ exports.whoAmICommand = function (options) { if (url) { Console.error("You haven't chosen your username yet. To pick it, go here:"); Console.error(); - Console.error(url); + Console.error(Console.url(url)); } else { // Won't happen in normal operation Console.error("You haven't chosen your username yet."); @@ -932,7 +932,7 @@ exports.registerOrLogIn = withAccountsConnection(function (connection) { var spinner = ['-', '\\', '|', '/']; lastLinePrinted = "Waiting for you to register on the web... " + spinner[animationFrame]; - Console.error(lastLinePrinted + "\r"); + Console.rawError(lastLinePrinted + "\r"); animationFrame = (animationFrame + 1) % spinner.length; }, 200); var stopSpinner = function () { diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index 7a8f635eff..e11b69d0b7 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -911,7 +911,9 @@ var CordovaRunner = function (projectContext, platformName, options) { "mobile apps in local development mode, except in the iOS " + "simulator. You can run the iOS simulator with 'meteor run ios'. " + "For additional workarounds, see " + - "https://github.com/meteor/meteor/wiki/OAuth-for-mobile-Meteor-clients."); + Console.url( + "https://github.com/meteor/meteor/wiki/" + + "OAuth-for-mobile-Meteor-clients.")); } }; @@ -1035,7 +1037,7 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { chalk.green("Could not open your project in Xcode.") + chalk.green("Try running again with the --verbose option.") + chalk.green("Instructions for running your app on an iOS device:")+ - chalk.cyan( + Console.url( "https://github.com/meteor/meteor/wiki/" + "How-to-run-your-app-on-an-iOS-device") ); @@ -1048,11 +1050,11 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { "Your project has been opened in Xcode so that you can run your " + "app on an iOS device. For further instructions, visit this " + "wiki page:") + - chalk.cyan( + Console.url( "https://github.com/meteor/meteor/wiki/" + "How-to-run-your-app-on-an-iOS-device" )); - Consoke.info(); + Console.info(); } else { verboseLog('Running emulator:', localCordova, args); var emulatorOptions = { verbose: options.verbose, cwd: cordovaPath }; @@ -1073,7 +1075,7 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { "Could not start the app on your device. Is it plugged in? " + "Try running again with the --verbose option. " + "Instructions for running your app on an Android device: ") + - chalk.cyan( + Console.url( "https://github.com/meteor/meteor/wiki/" + "How-to-run-your-app-on-an-Android-device") ); @@ -1284,7 +1286,7 @@ var checkAgreePlatformTerms = function (platform, name) { verboseLog("Error while downloading license terms: " + e); // most likely we don't have a net connection - Console.warn("Unable to download license terms for " + name + ". " + + Console.warn("Unable to download license terms for " + name + ".\n" + "Please make sure you are online.\n"); throw new main.ExitWithCode(2); } @@ -2938,8 +2940,8 @@ main.registerCommand({ // explain why we can't remove server or browser platforms if (_.contains(projectContextModule.PlatformList.DEFAULT_PLATFORMS, platform)) { - Console.info(platform + ": cannot remove platform " + - "in this version of Meteor\n"); + Console.warn( + platform + ": cannot remove platform in this version of Meteor"); return; } @@ -2950,7 +2952,7 @@ main.registerCommand({ return; } - Console.info(platform + ": platform is not in this project."); + Console.error(platform + ": platform is not in this project"); }); if (! changed) { @@ -3098,7 +3100,7 @@ main.registerCommand({ } openUrl(url); Console.info( - "Please follow the instructions here:\n" + Console.bold(url) + "\n"); + "Please follow the instructions here:\n" + Console.url(url) + "\n"); } else { Console.info("We don't have installation instructions for your platform"); } diff --git a/tools/commands-packages.js b/tools/commands-packages.js index 694b50f778..c40fc9af5d 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -59,7 +59,7 @@ var refreshOfficialCatalogOrDie = function (options) { var explainIfRefreshFailed = function () { if (catalog.official.offline || catalog.refreshFailed) { - Console.info("Your package catalog may be out of date. " + + Console.info("Your package catalog may be out of date.\n" + "Please connect to the internet and try again."); } }; @@ -391,7 +391,7 @@ main.registerCommand({ Console.info( "For more information on binary ABIs and consistent builds, see:"); Console.info( - "https://github.com/meteor/meteor/wiki/Build-Machines", + Console.url("https://github.com/meteor/meteor/wiki/Build-Machines"), Console.options({ indent: 2 }) ); Console.info(); @@ -977,7 +977,7 @@ main.registerCommand({ packageClient.callPackageServer( conn, '_changeReadmeURL', name, version, url); Console.info( - "Setting the readme of", name + "@" + version, "to", url); + "Setting the readme of", name + "@" + version, "to", Console.url(url)); }); } } @@ -1030,6 +1030,7 @@ main.registerCommand({ var versionVisible = function (record) { return options['show-old'] || !record.unmigrated; }; + var INDENT = 6; var full = options.args[0].split('@'); var name = full[0]; @@ -1134,15 +1135,15 @@ main.registerCommand({ "Architectures: ", formatAsList( buildArchitectures, { formatter: formatArchitecture }), - Console.options({ indent: 6 })); + Console.options({ indent: INDENT })); } // XXX: else show "no architectures"? if (v.packages) { - Console.info("tool: " + v.tool, Console.options({ indent: 6 })); - Console.info("packages:", Console.options({ indent: 6 })); + Console.info("tool: " + v.tool, Console.options({ indent: INDENT })); + Console.info("packages:", Console.options({ indent: INDENT })); _.each(v.packages, function(pv, pn) { - Console.info(pn + "@" + pv, Console.options({ indent: 6 })); + Console.info(pn + "@" + pv, Console.options({ indent: INDENT })); }); } }); @@ -1636,8 +1637,7 @@ var maybeUpdateRelease = function (options) { // not try to patch you to an unfriendly release. So, either way, as far // as we are concerned you are at the 'latest patch version' if (!patchRecord || !patchRecord.recommended ) { - Console.error( - "You are at the latest patch version."); + Console.error("You are at the latest patch version."); return 0; } // Great, we found a patch version. You can only have one latest patch for @@ -1686,7 +1686,7 @@ var maybeUpdateRelease = function (options) { Console.debug( "Update to release", releaseTrack + "@" + versionToTry, "is impossible:"); - Console.debug(messages.formatMessages()); + Console.debug(messages.formatMessages()); return false; } @@ -2656,15 +2656,14 @@ main.registerCommand({ var status = options.success ? "successfully" : "unsuccessfully"; // XXX: This should probably use progress bars instead. _.each(versions, function (version) { - var migrating = "Setting " - + name + "@" + version + " as " + - status + " migrated ..."; - process.stdout.write(migrating + "\r"); + Console.rawInfo( + "Setting " + name + "@" + version + " as " + + status + " migrated ... "); packageClient.callPackageServer( conn, '_changeVersionMigrationStatus', name, version, !options.success); - Console.info(migrating + "done."); + Console.info("done."); }); } catch (err) { packageClient.handlePackageServerConnectionError(err); @@ -2711,9 +2710,7 @@ main.registerCommand({ try { // XXX: This output should probably use progress bars instead! - var setting = - "Setting README of " + name + "@" + version + " to " + url + " ..."; - process.stdout.write(setting + "\r"); + Console.rawInfo("Setting README of " + name + "@" + version + " to " + url + " ..."); packageClient.callPackageServer( conn, '_changeReadmeURL', diff --git a/tools/commands.js b/tools/commands.js index 0ddf9a4821..9e49de68e7 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -88,7 +88,7 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { var archinfo = require('./archinfo.js'); - Console.info(archinfo.host()); + Console.rawInfo(archinfo.host() + "\n); }); // Prints the current release in use. Note that if there is not @@ -134,8 +134,8 @@ main.registerCommand({ Console.error("none"); return 1; } else { - Console.info(release.current.name); - Console.info(files.getToolsVersion()); + Console.rawInfo(release.current.name + "\n); + Console.rawInfo(files.getToolsVersion() + "\n"); return 0; } }); @@ -659,7 +659,7 @@ main.registerCommand(_.extend({ name: 'bundle', hidden: true "build for multiple platforms and outputs a directory instead of " + "a single tarball. See " + Console.command("'meteor help build'") + "for more information."); - Console.error();x + Console.error(); return buildCommand(_.extend(options, { _serverOnly: true })); }); @@ -760,8 +760,8 @@ var buildCommand = function (options) { // means the first step to getting there is going up a level. if (relative.substr(0, 3) !== ('..' + path.sep)) { Console.warn(); - Console.warn("Warning: The output directory is under your source tree."); - Console.warn( + Console.labelWarn( + "Warning: The output directory is under your source tree.",g "Your generated files may get interpreted as source code!", "Consider building into a different directory instead (" + Console.command("meteor build ../output") + ")", @@ -950,10 +950,10 @@ main.registerCommand({ Console.error(); Console.error("To reset a deployed application use"); Console.error( - "meteor deploy --delete appname", Console.options({ indent: 2 })); + Console.command("meteor deploy --delete appname"), Console.options({ indent: 2 })); Console.error("followed by"); Console.error( - "meteor deploy appname", Console.options({ indent: 2 })); + Console.command("meteor deploy appname"), Console.options({ indent: 2 })); return 1; } @@ -1060,8 +1060,7 @@ main.registerCommand({ if (options['override-architecture-with-local']) { Console.warn(); Console.labelWarn( - "OVERRIDING DEPLOY ARCHITECTURE WITH LOCAL ARCHITECTURE."); - Console.arrowInfo( + "OVERRIDING DEPLOY ARCHITECTURE WITH LOCAL ARCHITECTURE.", "If your app contains binary code, it may break in unexpected " + "and terrible ways."); buildArch = archinfo.host(); diff --git a/tools/console.js b/tools/console.js index 63d1644878..242bb88956 100644 --- a/tools/console.js +++ b/tools/console.js @@ -51,9 +51,9 @@ var FALLBACK_STATUS = ''; // If there is a part of the larger text, and we really want to make sure that // it doesn't get split up, we will replace the space with a utf character that -// we are not likely to use anywhere else. This one looks like the sun! We -// intentionally want to NOT use a space-like character: it should be obvious -// that something has gone wrong if this ever gets printed. +// we are not likely to use anywhere else. This one looks like the a BLACK SUN +// WITH RAYS. We intentionally want to NOT use a space-like character: it should +// be obvious that something has gone wrong if this ever gets printed. var SPACE_REPLACEMENT = '\u2600'; // In Javascript, replace only replaces the first occurance and this is the // proposed alternative. @@ -486,12 +486,6 @@ var Console = function (options) { // Legacy helpers self.stdout = {}; self.stderr = {}; - self.stdout.write = function (msg) { - self._print(LEVEL_INFO, msg); - }; - self.stderr.write = function (msg) { - self._print(LEVEL_WARN, msg); - }; self._stream = process.stdout; @@ -633,19 +627,19 @@ _.extend(Console.prototype, { _parseVariadicInput: function (args) { var self = this; var msgArgs; - var opts; + var options; // If the last argument is an instance of ConsoleOptions, then we should // separate it out, and only send the first N-1 arguments to be parsed as a // message. if (_.last(args) instanceof ConsoleOptions) { msgArgs = _.initial(args); - opts = _.last(args).options; + options = _.last(args).options; } else { msgArgs = args; - opts = {}; + options = {}; } var message = self._format(msgArgs); - return { message: message, opts: opts }; + return { message: message, opts: options }; }, isLevelEnabled: function (levelCode) { @@ -669,7 +663,7 @@ _.extend(Console.prototype, { self._print(LEVEL_DEBUG, message); }, - // By default, Console.debug automatically line wrapps the output. + // By default, Console.debug automatically line wraps the output. // // Takes in an optional Console.options({}) argument at the end, with the // following keys: @@ -709,7 +703,6 @@ _.extend(Console.prototype, { var self = this; if (! self.isInfoEnabled()) { return; } - var message = self._prettifyMessage(arguments); self._print(LEVEL_INFO, message); }, @@ -760,8 +753,8 @@ _.extend(Console.prototype, { var self = this; var parsedArgs = self._parseVariadicInput(msgArguments); var wrapOpts = { - indent: parsedArgs.opts.indent, - bulletPoint: parsedArgs.opts.bulletPoint + indent: parsedArgs.options.indent, + bulletPoint: parsedArgs.options.bulletPoint }; var wrappedMessage = self._wrapText(parsedArgs.message, wrapOpts); @@ -816,32 +809,31 @@ _.extend(Console.prototype, { }, // A wrapper around Console.info. Prints the message out in green (if pretty), - // with the ascii checkmark as the bullet point in front of it. + // with the CHECKMARK as the bullet point in front of it. success: function (message) { var self = this; if (! self._pretty) { return self.info(message); } - - var checkmark = chalk.green('\u2713'); + var checkmark = chalk.green('\u2713'); // CHECKMARK return self.info( chalk.green(message), - self.options({ bulletPoint: checkmark })); + self.options({ bulletPoint: checkmark + " "})); }, // Wrapper around Console.info. Prints the message out in red (if pretty) - // with the ascii x as the bullet point in front of it. + // with the BALLOT X as the bullet point in front of it. failInfo: function (message) { var self = this; - return self._fail(message, self.info); + return self._fail(message, "info"); }, // Wrapper around Console.warn. Prints the message out in red (if pretty) // with the ascii x as the bullet point in front of it. failWarn: function (message) { var self = this; - return self._fail(message, self.warn); + return self._fail(message, "warn"); }, // Print the message in red (if pretty) with an x bullet point in front of it. @@ -853,15 +845,15 @@ _.extend(Console.prototype, { } var xmark = chalk.red('\u2717'); - return printFn( + return self[printFn]( chalk.red(message), - self.options({ bulletPoint: xmark })); + self.options({ bulletPoint: xmark + " " })); }, // Wrapper around Console.warn that prints a large "WARNING" label in front. labelWarn: function (message) { var self = this; - return self.warn(message, self.options({ bulletPoint: "WARNING" })); + return self.warn(message, self.options({ bulletPoint: "WARNING: " })); }, // Wrappers around Console functions to prints an "=> " in front. Optional @@ -881,10 +873,9 @@ _.extend(Console.prototype, { _arrowPrint: function(printFn, message, indent) { var self = this; indent = indent || 0; - var myIndent = Array(indent + 1).join(" "); return self[printFn]( message, - self.options({ bulletPoint: myIndent + ARROW })); + self.options({ bulletPoint: ARROW, indent: indent })); }, // A wrapper around console.error. Given an error and some background @@ -931,14 +922,23 @@ _.extend(Console.prototype, { // If pretty print is on, this will also bold the commands. command: function (message) { var self = this; - var noBlanks = - replaceAll(message, ' ', SPACE_REPLACEMENT); - return this.bold(noBlanks); + var unwrapped = self.doNotWrap(message); + return self.bold(unwrapped); }, // Underline the URLs (if pretty print is on). url: function (message) { - return this.underline(message); + var self = this; + var unwrapped = self.doNotWrap(message); + return self.underline(unwrapped); + }, + + // Do not wrap this substring when you send it into a non-raw print function. + // DO NOT print the result of this call with a raw function. + doNotWrap: function (message) { + var noBlanks = + replaceAll(message, ' ', SPACE_REPLACEMENT); + return noBlanks; }, // A wrapper around the underline functionality of chalk. @@ -1007,8 +1007,10 @@ _.extend(Console.prototype, { // // text: the text to wrap // options: + // - bulletPoint: start the first line with a given string, then offset the - // subsequent lines by the length of that string. For example: + // subsequent lines by the length of that string. For example, if the + // bulletpoint is " => ", we would get: // " => some long message starts here // and then continues here." // - indent: offset the entire string by a specific number of @@ -1038,7 +1040,7 @@ _.extend(Console.prototype, { } // Get the maximum width, or if we are not running in a terminal (self-test, - // for exmaple), default to 80 columns. + // for example), default to 80 columns. var max = self.width(); // Wrap the text using the npm wordwrap library. diff --git a/tools/deploy.js b/tools/deploy.js index 91f68ce7e7..3fc4c8013d 100644 --- a/tools/deploy.js +++ b/tools/deploy.js @@ -320,7 +320,7 @@ var canonicalizeSite = function (site) { if (parsed.pathname != '/' || parsed.hash || parsed.query) { Console.info( "Sorry, Meteor does not yet support specific path URLs, such as " + - "http://www.example.com/blog . Please specify the root of a domain."); + Console.url("http://www.example.com/blog") + " . Please specify the root of a domain."); return false; } @@ -723,7 +723,7 @@ var claim = function (site) { Console.error( "You need to set a password on your Meteor developer account before", "you can claim sites. You can do that here in under a minute:"); - Console.error(auth.registrationUrl()); + Console.error(Console.url(auth.registrationUrl())); Console.error(); } else { Console.error("Couldn't claim site: " + result.errorMessage); diff --git a/tools/main.js b/tools/main.js index 01ec8594fc..6403888032 100644 --- a/tools/main.js +++ b/tools/main.js @@ -1159,7 +1159,7 @@ Fiber(function () { Console.error( Console.command(commandName) + ": the --" + Console.command(optionName) + " option is required."); - Console.error(longHelp(commandName)); + Console.rawError(longHelp(commandName)); process.exit(1); } } @@ -1168,7 +1168,8 @@ Fiber(function () { // Check for unrecognized options. if (_.keys(rawOptions).length > 0) { Console.error( - Console.command(_.keys(rawOptions)[0]) + ": unknown option.\n" + + Console.command(_.keys(rawOptions)[0]) + ": unknown option."); + Console.rawError( longHelp(commandName)); process.exit(1); } @@ -1176,14 +1177,16 @@ Fiber(function () { // Check argument count. if (options.args.length < command.minArgs) { Console.error( - Console.command(commandName) + ": not enough arguments.\n" + + Console.command(commandName) + ": not enough arguments."); + Console.rawError( longHelp(commandName)); process.exit(1); } if (options.args.length > command.maxArgs) { Console.error( - Console.command(commandName) + ": too many arguments.\n" + + Console.command(commandName) + ": too many arguments."); + Console.rawErro( longHelp(commandName)); process.exit(1); } @@ -1261,7 +1264,7 @@ Fiber(function () { // app's usual release by using a checkout, print a reminder banner. Console.arrowWarn( "Running Meteor from a checkout -- overrides project version " + - Console.command("(" + appReleaseFile.displayReleaseName + ")")); + Console.doNotWrap("(" + appReleaseFile.displayReleaseName + ")")); } // Now that we're ready to start executing the command, if we are in diff --git a/tools/run-all.js b/tools/run-all.js index 0f93697340..479ebe0494 100644 --- a/tools/run-all.js +++ b/tools/run-all.js @@ -334,14 +334,14 @@ exports.run = function (options) { if (result.outcome === "outdated-cordova-plugins") { Console.error( - "Your app's Cordova plugins have changed.", + "Your app's Cordova plugins have changed.\n", "Restart meteor to use the new set of plugins."); return 254; } if (result.outcome === "outdated-cordova-platforms") { Console.error( - "Your app's platforms have changed.", + "Your app's platforms have changed.\n", "Restart meteor to use the new set of platforms."); return 254; } diff --git a/tools/run-log.js b/tools/run-log.js index a87af15606..b2902445fc 100644 --- a/tools/run-log.js +++ b/tools/run-log.js @@ -121,15 +121,7 @@ _.extend(RunLog.prototype, { // Process the options. By default, we want to wordwrap the message with // Console.info. If we ask for raw output, then we don't want to do that. If // we ask for an arrow, we want to wrap around with => as the bulletPoint. - var printFn; - if (options.arrow) { - printFn = Console.arrowInfo; - } else { - printFn = Console.info; - } - - // Print the message to the logs. - printFn(msg); + Console[options.arrow ? 'arrowInfo' : 'info'](msg); }, // Write a message to the terminal that will get overwritten by the From 0b4cdc6e12cdfb6e4a7fa89d67795bc6425b4dd5 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 18:42:36 -0800 Subject: [PATCH 06/16] minor cleanup - renamed an argument from 'opts' to 'options' in the comments as well. - fixed some forgotten quotes. --- tools/commands.js | 6 +++--- tools/console.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/commands.js b/tools/commands.js index 9e49de68e7..dad51e1cb0 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -88,7 +88,7 @@ main.registerCommand({ catalogRefresh: new catalog.Refresh.Never() }, function (options) { var archinfo = require('./archinfo.js'); - Console.rawInfo(archinfo.host() + "\n); + Console.rawInfo(archinfo.host() + "\n"); }); // Prints the current release in use. Note that if there is not @@ -134,7 +134,7 @@ main.registerCommand({ Console.error("none"); return 1; } else { - Console.rawInfo(release.current.name + "\n); + Console.rawInfo(release.current.name + "\n"); Console.rawInfo(files.getToolsVersion() + "\n"); return 0; } @@ -761,7 +761,7 @@ var buildCommand = function (options) { if (relative.substr(0, 3) !== ('..' + path.sep)) { Console.warn(); Console.labelWarn( - "Warning: The output directory is under your source tree.",g + "Warning: The output directory is under your source tree.", "Your generated files may get interpreted as source code!", "Consider building into a different directory instead (" + Console.command("meteor build ../output") + ")", diff --git a/tools/console.js b/tools/console.js index 242bb88956..b842661355 100644 --- a/tools/console.js +++ b/tools/console.js @@ -621,7 +621,7 @@ _.extend(Console.prototype, { // optional ConsoleOptions argument at the end. // // Returns an object with keys: - // - opts: The options that were passed in, or an empty object. + // - options: The options that were passed in, or an empty object. // - message: Arguments to the original function, parsed as a string. // _parseVariadicInput: function (args) { @@ -639,7 +639,7 @@ _.extend(Console.prototype, { options = {}; } var message = self._format(msgArgs); - return { message: message, opts: options }; + return { message: message, options: options }; }, isLevelEnabled: function (levelCode) { From 6bf699d5d9a9c662d05108ce56eeaeaad7febf82 Mon Sep 17 00:00:00 2001 From: ekatek Date: Sat, 6 Dec 2014 19:13:51 -0800 Subject: [PATCH 07/16] Fixed some mistypes --- tools/main.js | 2 +- tools/run-log.js | 2 +- tools/tests/cordova-platforms.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/main.js b/tools/main.js index 6403888032..e88ec12d81 100644 --- a/tools/main.js +++ b/tools/main.js @@ -1186,7 +1186,7 @@ Fiber(function () { if (options.args.length > command.maxArgs) { Console.error( Console.command(commandName) + ": too many arguments."); - Console.rawErro( + Console.rawError( longHelp(commandName)); process.exit(1); } diff --git a/tools/run-log.js b/tools/run-log.js index b2902445fc..8728d20ca2 100644 --- a/tools/run-log.js +++ b/tools/run-log.js @@ -93,7 +93,7 @@ _.extend(RunLog.prototype, { self._clearSpecial(); if (self.rawLogs) - Console[isStderr ? "stderr" : "stdout"].write(line + "\n"); + Console[isStderr ? "rawError" : "rawInfo"](line + "\n"); else Console.rawInfo(Log.format(obj, { color: true }) + "\n"); diff --git a/tools/tests/cordova-platforms.js b/tools/tests/cordova-platforms.js index b831486014..42c9f7b1e5 100644 --- a/tools/tests/cordova-platforms.js +++ b/tools/tests/cordova-platforms.js @@ -35,7 +35,7 @@ selftest.define("add cordova platforms", function () { run.match("added"); run = s.run("remove-platform", "foo"); - run.match("foo: platform is not"); + run.matchErr("foo: platform is not"); run = s.run("remove-platform", "android"); run.match("removed"); From 714ed749e72d31c6a98b94f27653d0814c69d2a0 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 11:57:36 -0800 Subject: [PATCH 08/16] for URLs replace spaces with %20 instead of just taking care to not wrap them --- tools/console.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/console.js b/tools/console.js index b842661355..755ca6981e 100644 --- a/tools/console.js +++ b/tools/console.js @@ -929,8 +929,14 @@ _.extend(Console.prototype, { // Underline the URLs (if pretty print is on). url: function (message) { var self = this; - var unwrapped = self.doNotWrap(message); - return self.underline(unwrapped); + // If we are going to print URLs with spaces, we should turn spaces into + // things browsers understand. + var unspaced = + replaceAll(message, ' ', '%20'); + // There is no need to call doNotWrap here, since that only handles spaces + // (and we have done that). If it ever handles other things, we should call + // it here. + return self.underline(unspaced); }, // Do not wrap this substring when you send it into a non-raw print function. From f4d9e5bff149eb02fbc182e9893d0dff9104ac8d Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 13:57:24 -0800 Subject: [PATCH 09/16] adding extra line breaks to some cordova output Glasser thinks it looks better with more newlines. I am not sure that's true -- but it looks kind of stern either way, so might as well add them in. --- tools/commands-cordova.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index e11b69d0b7..629a0d3285 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1033,10 +1033,10 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { execFileSyncOrThrow('sh', args); } catch (err) { Console.error(); + Console.error(chalk.green("Could not open your project in Xcode.")); + Console.error(chalk.green("Try running again with the --verbose option.")); Console.error( - chalk.green("Could not open your project in Xcode.") + - chalk.green("Try running again with the --verbose option.") + - chalk.green("Instructions for running your app on an iOS device:")+ + chalk.green("Instructions for running your app on an iOS device: ") + Console.url( "https://github.com/meteor/meteor/wiki/" + "How-to-run-your-app-on-an-iOS-device") @@ -1049,7 +1049,7 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { chalk.green( "Your project has been opened in Xcode so that you can run your " + "app on an iOS device. For further instructions, visit this " + - "wiki page:") + + "wiki page: ") + Console.url( "https://github.com/meteor/meteor/wiki/" + "How-to-run-your-app-on-an-iOS-device" @@ -1071,33 +1071,32 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { if (err && platform === "android" && isDevice) { Console.error(); Console.error( - chalk.green( - "Could not start the app on your device. Is it plugged in? " + - "Try running again with the --verbose option. " + - "Instructions for running your app on an Android device: ") + + chalk.green("Could not start the app on your device. Is it plugged in?"); + Console.error("Try running again with the --verbose option."); + Console.error( + chalk.green("Instructions for running your app on an Android device: ") + Console.url( "https://github.com/meteor/meteor/wiki/" + - "How-to-run-your-app-on-an-Android-device") - ); + "How-to-run-your-app-on-an-Android-device")); Console.error(); } else if (err && platform === "android") { Console.error(); Console.error( - chalk.green("Could not start the app in the Android emulator."), + chalk.green("Could not start the app in the Android emulator.\n"), chalk.green("Try running again with the --verbose option.") ); Console.error(); } else if (err && platform === "ios") { Console.error(); Console.error( - chalk.green("Could not start the app in the iOS simulator."), + chalk.green("Could not start the app in the iOS simulator.\n"), chalk.green("Try running again with the --verbose option.") ); Console.error(); } else if (err) { Console.error(); Console.error( - chalk.green("Could not start your app."), + chalk.green("Could not start your app.\n"), chalk.green("Try running again with the --verbose option.") ); Console.error(); From bcf6b200e1ab14c5aae62c3f7cd3349c42a027a4 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 14:15:54 -0800 Subject: [PATCH 10/16] introducing and using Console.directory I think that this makes the API too complicated, but we might want to treat directories the same way that we do commands (use some chalk when needed, do not wrap, etc). This introduces the Console.directory function. Of course, it is not really clear to me what happens if a directory is inside a command. (ex: 'cd '). Right now, there is no difference. It makes sense to me that we might want to keep the entire command the same style, so I am not wrapping those in additional Console.directory units for now. --- tools/commands-cordova.js | 11 +++++++---- tools/commands-packages.js | 29 ++++++++++++++--------------- tools/console.js | 8 ++++++++ 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index 629a0d3285..1b48e31755 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1071,7 +1071,7 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { if (err && platform === "android" && isDevice) { Console.error(); Console.error( - chalk.green("Could not start the app on your device. Is it plugged in?"); + chalk.green("Could not start the app on your device. Is it plugged in?")); Console.error("Try running again with the --verbose option."); Console.error( chalk.green("Instructions for running your app on an Android device: ") + @@ -1898,13 +1898,15 @@ _.extend(IOS.prototype, { _.each(['5.0', '5.0.1', '5.1', '6.0', '6.1'], function (version) { if (self.isSdkInstalled(version) && log) { Console.warn( - "An old version of the iPhone SDK is installed (" + version + ");", + "An old version of the iPhone SDK is installed", + Console.doNotWrap("(" + version + ")") + ";", "you should probably delete it. With SDK versions prior to 7.0", "installed, your apps can't be published to the App Store.", "Moreover, some Cordova plugins are incompatible with this SDK.", "You can remove it by deleting this directory: "); Console.warn( - self.getDirectoryForSdk(version), Console.options({ indent: 4 })); + Console.directory(self.getDirectoryForSdk(version)), + Console.options({ indent: 4 })); // Not really a failure; just warn... } }); @@ -1951,7 +1953,8 @@ _.extend(Android.prototype, { } Console.info( - "Can't determine acceleration for unknown host: ", archinfo.host()); + "Can't determine acceleration for unknown host: ", + Console.doNotWrap(archinfo.host())); return undefined; }, diff --git a/tools/commands-packages.js b/tools/commands-packages.js index c40fc9af5d..d354cac56e 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -1663,7 +1663,7 @@ var maybeUpdateRelease = function (options) { // that track, so we are done. Console.info( "This project is already at " + - projectContext.releaseFile.displayReleaseName + + Console.doNotWrap(projectContext.releaseFile.displayReleaseName) + ", which is newer than the latest release."); return 0; } @@ -2287,9 +2287,9 @@ main.registerCommand({ Console.info("The maintainers for " + name + " are:"); _.each(record.maintainers, function (user) { if (! user || !user.username) - Console.info(""); + Console.rawInfo("" + "\n"); else - Console.info(user.username + ""); + Console.rawInfo(user.username + "\n"); }); return 0; }); @@ -2334,11 +2334,11 @@ main.registerCommand({ toolPackage, toolVersion); if (!toolPkgBuilds) { // XXX this could also mean package unknown. - Console.error('Tool version unknown: ' + release.tool + ''); + Console.error('Tool version unknown: ' + release.tool); return 1; } if (!toolPkgBuilds.length) { - Console.error('Tool version has no builds: ' + release.tool + ''); + Console.error('Tool version has no builds: ' + release.tool); return 1; } @@ -2358,8 +2358,8 @@ main.registerCommand({ }); Console.error( - 'Building bootstrap tarballs for architectures ' + - osArches.join(', ') + ''); + 'Building bootstrap tarballs for architectures ' + osArches.join(', ')); + // Before downloading anything, check that the catalog contains everything we // need for the OSes that the tool is built for. var messages = buildmessage.capture(function () { @@ -2378,7 +2378,7 @@ main.registerCommand({ }); if (messages.hasMessages()) { - Console.error("\n" + messages.formatMessages()); + Console.printMessages(messages); return 1; }; @@ -2482,8 +2482,7 @@ main.registerCommand({ var bannersData = fs.readFileSync(bannersFile, 'utf8'); bannersData = JSON.parse(bannersData); } catch (e) { - Console.error("Could not parse banners file: "); - Console.error(e.message + ""); + Console.error("Could not parse banners file: " + e.message); return 1; } if (!bannersData.track) { @@ -2603,12 +2602,12 @@ main.registerCommand({ } try { - Console.info( - "Changing homepage on " + Console.rawInfo( + "Changing homepage on " + name + " to " + url + "..."); packageClient.callPackageServer(conn, '_changePackageHomepage', name, url); - Console.info("Done!"); + Console.info(" done"); } catch (err) { packageClient.handlePackageServerConnectionError(err); return 1; @@ -2710,12 +2709,12 @@ main.registerCommand({ try { // XXX: This output should probably use progress bars instead! - Console.rawInfo("Setting README of " + name + "@" + version + " to " + url + " ..."); + Console.rawInfo("Setting README of " + name + "@" + version + " to " + url + " ... "); packageClient.callPackageServer( conn, '_changeReadmeURL', name, version, url); - Console.info(setting + " done."); + Console.info("done."); } catch (err) { packageClient.handlePackageServerConnectionError(err); return 1; diff --git a/tools/console.js b/tools/console.js index 755ca6981e..f75d218d8d 100644 --- a/tools/console.js +++ b/tools/console.js @@ -939,6 +939,14 @@ _.extend(Console.prototype, { return self.underline(unspaced); }, + directory: function (message) { + var self = this; + // XXX: Consider automatically escaping spaces. + // (Want to make sure that we don't escape a space twice though) + var unwrapped = self.doNotWrap(message); + return self.bold(unwrapped); + }, + // Do not wrap this substring when you send it into a non-raw print function. // DO NOT print the result of this call with a raw function. doNotWrap: function (message) { From f3e3dc7bca29548b6d682a8bb0865ebce0fdceaf Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 14:44:36 -0800 Subject: [PATCH 11/16] rename doNotWrap to noWrap in console.js' --- tools/commands-cordova.js | 4 ++-- tools/commands-packages.js | 2 +- tools/console.js | 11 +++++------ tools/main.js | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index 1b48e31755..9f24df5302 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1899,7 +1899,7 @@ _.extend(IOS.prototype, { if (self.isSdkInstalled(version) && log) { Console.warn( "An old version of the iPhone SDK is installed", - Console.doNotWrap("(" + version + ")") + ";", + Console.noWrap("(" + version + ")") + ";", "you should probably delete it. With SDK versions prior to 7.0", "installed, your apps can't be published to the App Store.", "Moreover, some Cordova plugins are incompatible with this SDK.", @@ -1954,7 +1954,7 @@ _.extend(Android.prototype, { Console.info( "Can't determine acceleration for unknown host: ", - Console.doNotWrap(archinfo.host())); + Console.noWrap(archinfo.host())); return undefined; }, diff --git a/tools/commands-packages.js b/tools/commands-packages.js index d354cac56e..5987f4e9a2 100644 --- a/tools/commands-packages.js +++ b/tools/commands-packages.js @@ -1663,7 +1663,7 @@ var maybeUpdateRelease = function (options) { // that track, so we are done. Console.info( "This project is already at " + - Console.doNotWrap(projectContext.releaseFile.displayReleaseName) + + Console.noWrap(projectContext.releaseFile.displayReleaseName) + ", which is newer than the latest release."); return 0; } diff --git a/tools/console.js b/tools/console.js index f75d218d8d..251c6a2c60 100644 --- a/tools/console.js +++ b/tools/console.js @@ -922,7 +922,7 @@ _.extend(Console.prototype, { // If pretty print is on, this will also bold the commands. command: function (message) { var self = this; - var unwrapped = self.doNotWrap(message); + var unwrapped = self.noWrap(message); return self.bold(unwrapped); }, @@ -933,7 +933,7 @@ _.extend(Console.prototype, { // things browsers understand. var unspaced = replaceAll(message, ' ', '%20'); - // There is no need to call doNotWrap here, since that only handles spaces + // There is no need to call noWrap here, since that only handles spaces // (and we have done that). If it ever handles other things, we should call // it here. return self.underline(unspaced); @@ -943,15 +943,14 @@ _.extend(Console.prototype, { var self = this; // XXX: Consider automatically escaping spaces. // (Want to make sure that we don't escape a space twice though) - var unwrapped = self.doNotWrap(message); + var unwrapped = self.noWrap(message); return self.bold(unwrapped); }, // Do not wrap this substring when you send it into a non-raw print function. // DO NOT print the result of this call with a raw function. - doNotWrap: function (message) { - var noBlanks = - replaceAll(message, ' ', SPACE_REPLACEMENT); + noWrap: function (message) { + var noBlanks = replaceAll(message, ' ', SPACE_REPLACEMENT); return noBlanks; }, diff --git a/tools/main.js b/tools/main.js index e88ec12d81..fb914b29bd 100644 --- a/tools/main.js +++ b/tools/main.js @@ -1264,7 +1264,7 @@ Fiber(function () { // app's usual release by using a checkout, print a reminder banner. Console.arrowWarn( "Running Meteor from a checkout -- overrides project version " + - Console.doNotWrap("(" + appReleaseFile.displayReleaseName + ")")); + Console.noWrap("(" + appReleaseFile.displayReleaseName + ")")); } // Now that we're ready to start executing the command, if we are in From 90859759137014b1475c6760343ddde0cfa7d103 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 14:54:36 -0800 Subject: [PATCH 12/16] automatically escape spaces when printing directories Directories should not wrap, but, also, we should make sure to automatically escape spaces ("/ab/a\ b.js" vs "/ab/a b.js"). Because we might need to deal with user input, we don't know if the user has already escaped the spaces before getting the string. As such, we should only escape spaces that haven't already been escaped. --- tools/console.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/console.js b/tools/console.js index 251c6a2c60..7b996f1b15 100644 --- a/tools/console.js +++ b/tools/console.js @@ -933,16 +933,20 @@ _.extend(Console.prototype, { // things browsers understand. var unspaced = replaceAll(message, ' ', '%20'); - // There is no need to call noWrap here, since that only handles spaces - // (and we have done that). If it ever handles other things, we should call - // it here. + // There is no need to call noWrap here, since that only handles spaces (and + // we have done that). If it ever handles things other than spaces, we + // should make sure to call it here. return self.underline(unspaced); }, directory: function (message) { var self = this; - // XXX: Consider automatically escaping spaces. - // (Want to make sure that we don't escape a space twice though) + // Escape any spaces that we don't already escape. + var escapedSpace = "\\ "; + message = _.map(message.split(escapedSpace), function (msg) { + return replaceAll(msg, ' ', escapedSpace); + }).join(escapedSpace); + // Make sure that we don't wrap this. var unwrapped = self.noWrap(message); return self.bold(unwrapped); }, From 42b0d9247dbc088ccfc4eb703e2051f3107cba77 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 18:30:52 -0800 Subject: [PATCH 13/16] remove the extra Warning sign --- tools/commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/commands.js b/tools/commands.js index dad51e1cb0..04bbf077a5 100644 --- a/tools/commands.js +++ b/tools/commands.js @@ -761,7 +761,7 @@ var buildCommand = function (options) { if (relative.substr(0, 3) !== ('..' + path.sep)) { Console.warn(); Console.labelWarn( - "Warning: The output directory is under your source tree.", + "The output directory is under your source tree.", "Your generated files may get interpreted as source code!", "Consider building into a different directory instead (" + Console.command("meteor build ../output") + ")", From 8d213d0cb02e214cd69e55954583a4e82c8dc4fe Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 18:34:38 -0800 Subject: [PATCH 14/16] fix some newline breaks --- tools/commands-cordova.js | 18 ++++++------------ tools/run-all.js | 10 ++++------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index 9f24df5302..d91ccfebf2 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1081,24 +1081,18 @@ var execCordovaOnPlatform = function (projectContext, platformName, options) { Console.error(); } else if (err && platform === "android") { Console.error(); - Console.error( - chalk.green("Could not start the app in the Android emulator.\n"), - chalk.green("Try running again with the --verbose option.") - ); + Console.error(chalk.green("Could not start the app in the Android emulator.")); + Console.error(chalk.green("Try running again with the --verbose option.")); Console.error(); } else if (err && platform === "ios") { Console.error(); - Console.error( - chalk.green("Could not start the app in the iOS simulator.\n"), - chalk.green("Try running again with the --verbose option.") - ); + Console.error(chalk.green("Could not start the app in the iOS simulator.")); + Console.error(chalk.green("Try running again with the --verbose option.")); Console.error(); } else if (err) { Console.error(); - Console.error( - chalk.green("Could not start your app.\n"), - chalk.green("Try running again with the --verbose option.") - ); + Console.error(chalk.green("Could not start your app.")); + Console.error(chalk.green("Try running again with the --verbose option.")); Console.error(); } diff --git a/tools/run-all.js b/tools/run-all.js index 479ebe0494..340a49fdc1 100644 --- a/tools/run-all.js +++ b/tools/run-all.js @@ -333,16 +333,14 @@ exports.run = function (options) { } if (result.outcome === "outdated-cordova-plugins") { - Console.error( - "Your app's Cordova plugins have changed.\n", - "Restart meteor to use the new set of plugins."); + Console.error("Your app's Cordova plugins have changed."); + Console.error("Restart meteor to use the new set of plugins."); return 254; } if (result.outcome === "outdated-cordova-platforms") { - Console.error( - "Your app's platforms have changed.\n", - "Restart meteor to use the new set of platforms."); + Console.error("Your app's platforms have changed."); + Console.error("Restart meteor to use the new set of platforms."); return 254; } From 07ff83f2c92cd780dc18cb29e84aec114956f185 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 18:37:49 -0800 Subject: [PATCH 15/16] rename Console.directory to Console.path, do not escape spaces Rename Console.directory to Console.path. Do not attempt to automatically escape spaces in file paths -- it is hard to define a function that does this only sometimes, rather than all the time. This is something that we could change later, once we have a better idea of when we use it. --- tools/commands-cordova.js | 2 +- tools/console.js | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tools/commands-cordova.js b/tools/commands-cordova.js index d91ccfebf2..f14653a9fc 100644 --- a/tools/commands-cordova.js +++ b/tools/commands-cordova.js @@ -1899,7 +1899,7 @@ _.extend(IOS.prototype, { "Moreover, some Cordova plugins are incompatible with this SDK.", "You can remove it by deleting this directory: "); Console.warn( - Console.directory(self.getDirectoryForSdk(version)), + Console.path(self.getDirectoryForSdk(version)), Console.options({ indent: 4 })); // Not really a failure; just warn... } diff --git a/tools/console.js b/tools/console.js index 7b996f1b15..1a2191de4c 100644 --- a/tools/console.js +++ b/tools/console.js @@ -939,13 +939,11 @@ _.extend(Console.prototype, { return self.underline(unspaced); }, - directory: function (message) { + // Format a filepath to not wrap. This does NOT automatically escape spaces + // (ie: add a slash in front so the user could copy paste the file path into a + // terminal). + path: function (message) { var self = this; - // Escape any spaces that we don't already escape. - var escapedSpace = "\\ "; - message = _.map(message.split(escapedSpace), function (msg) { - return replaceAll(msg, ' ', escapedSpace); - }).join(escapedSpace); // Make sure that we don't wrap this. var unwrapped = self.noWrap(message); return self.bold(unwrapped); From c8e2c9f9d9966a936138ab7edba79c9419207216 Mon Sep 17 00:00:00 2001 From: ekatek Date: Mon, 8 Dec 2014 18:44:28 -0800 Subject: [PATCH 16/16] change to the right slash --- tools/console.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/console.js b/tools/console.js index 1a2191de4c..c16e09ee7e 100644 --- a/tools/console.js +++ b/tools/console.js @@ -1005,7 +1005,7 @@ _.extend(Console.prototype, { }); var level = options.level || self.LEVEL_INFO; - out += "/n"; + out += "\n"; self._print(level, out); return out;