Support meteor <command> for any dev_bundle/bin/<command> executable.

This will make it easier to use tools like https://yarnpkg.com/ with the
right version of Node, etc.

With this commit, here's all you have to do:

  meteor npm install -g yarnpkg

Then test that it works:

  meteor yarn info

Note that any commands registered by Meteor itself will not be honored.
This commit is contained in:
Ben Newman
2016-10-12 11:41:35 -04:00
parent ebecfeeb3e
commit 4300d261f3
4 changed files with 85 additions and 10 deletions

View File

@@ -48,6 +48,9 @@
sensitive to non-reproducible factors in the external environment.
https://github.com/meteor/meteor/pull/7668/commits/3313180a6ff33ee63602f7592a9506012029e919
* The `meteor <command> ...` syntax will now work for any command
installed in `dev_bundle/bin`, except for Meteor's own commands.
* The `meteor test` command now supports the `--no-release-check` flag.
https://github.com/meteor/meteor/pull/7668/commits/7097f78926f331fb9e70a06300ce1711adae2850

View File

@@ -1,11 +1,8 @@
// Note that this file is required before we install our Babel hooks in
// ../tool-env/install-babel.js, so we can't use ES2015+ syntax here.
var fs = require("fs");
var path = require("path");
var win32Extensions = {
node: ".exe",
npm: ".cmd"
};
// The dev_bundle/bin command has to come immediately after the meteor
// command, as in `meteor npm` or `meteor node`, because we don't want to
@@ -14,22 +11,22 @@ var devBundleBinCommand = process.argv[2];
var args = process.argv.slice(3);
function getChildProcess() {
if (! win32Extensions.hasOwnProperty(devBundleBinCommand)) {
if (typeof devBundleBinCommand !== "string") {
return Promise.resolve(null);
}
var helpers = require("./dev-bundle-bin-helpers.js");
if (process.platform === "win32") {
devBundleBinCommand += win32Extensions[devBundleBinCommand];
}
return Promise.all([
helpers.getDevBundle(),
helpers.getEnv()
]).then(function (devBundleAndEnv) {
var devBundleDir = devBundleAndEnv[0];
var cmd = path.join(devBundleDir, "bin", devBundleBinCommand);
var cmd = helpers.getCommand(devBundleBinCommand, devBundleDir);
if (! cmd) {
return null;
}
var env = devBundleAndEnv[1];
var child = require("child_process").spawn(cmd, args, {
stdio: "inherit",

View File

@@ -1,12 +1,65 @@
var fs = require("fs");
var path = require("path");
var files = require("../fs/mini-files.js");
var isWindows = process.platform === "win32";
var extensions = isWindows ? [".cmd", ".exe"] : [""];
var hasOwn = Object.prototype.hasOwnProperty;
function getDevBundle() {
return require("./dev-bundle.js");
}
exports.getDevBundle = getDevBundle;
exports.getCommand = function (name, devBundleDir) {
var result = null;
// Strip leading and/or trailing whitespace.
name = name.replace(/^\s+|\s+$/g, "");
if (! isValidCommand(name, devBundleDir)) {
return result;
}
extensions.some(function (ext) {
var cmd = path.join(devBundleDir, "bin", name + ext);
try {
if (fs.statSync(cmd).isFile()) {
result = cmd;
return true;
}
} catch (e) {
return false;
}
});
return result;
};
function isValidCommand(name, devBundleDir) {
if (name === "node" ||
name === "npm") {
return true;
}
if (! name || name.charAt(0) === ".") {
// Disallow empty commands and commands that start with a period.
return false;
}
var meteorCommandsJsonPath =
path.join(devBundleDir, "bin", ".meteor-commands.json");
try {
var meteorCommands = require(meteorCommandsJsonPath);
} catch (e) {
return false;
}
// If `meteor <name>` is already a Meteor command, don't let anything in
// dev_bundle/bin override it.
return ! hasOwn.call(meteorCommands, name);
}
exports.getEnv = function (options) {
var devBundle = options && options.devBundle;
var devBundlePromise = typeof devBundle === "string"

View File

@@ -291,6 +291,28 @@ require('./commands-packages-query.js');
require('./commands-cordova.js');
require('./commands-aliases.js');
///////////////////////////////////////////////////////////////////////////////
// Record all the top-level commands as JSON
///////////////////////////////////////////////////////////////////////////////
export const meteorCommandsJsonPath = files.pathJoin(
files.getDevBundle(), "bin", ".meteor-commands.json"
);
export function dumpMeteorCommands() {
const all = Object.create(null);
Object.keys(commands).forEach(name => all[name] = true);
const json = JSON.stringify(all, null, 2);
files.writeFile(meteorCommandsJsonPath, json + "\n");
return all;
}
if (files.inCheckout()) {
// If we're running Meteor from a checkout, dump the commands every
// time, so that the file remains up to date.
dumpMeteorCommands();
}
///////////////////////////////////////////////////////////////////////////////
// Long-form help
///////////////////////////////////////////////////////////////////////////////