Improve meteor shell command evaluation.

Specific improvements:

- Parentheses are now stripped from commands that look like named classes
  so that they will be treated as class declarations rather than as named
  class expressions.

- When the `ecmascript` package is installed, `compileForShell` errors are
  now exposed to the `evalCommand` callback.

- Instead of using `vm.runInThisContext` to parse and evaluate commands at
  the same time, `evalCommand` now parses commands by creating a new
  `vm.Script` and later evaluates them using `script.runInThisContext()`,
  so that `SyntaxError`s can be reported immediately. Fixes #5131.
This commit is contained in:
Ben Newman
2015-09-11 18:12:40 -04:00
parent 6a8ca4c1f6
commit d536124bcc

View File

@@ -266,21 +266,54 @@ var evalCommandPromise = Promise.resolve();
function evalCommand(command, context, filename, callback) {
if (Package.ecmascript) {
var noParens = stripParens(command);
if (noParens !== command) {
var classMatch = /^\s*class\s+(\w+)/.exec(noParens);
if (classMatch && classMatch[1] !== "extends") {
// If the command looks like a named ES2015 class, we remove the
// extra layer of parentheses added by the REPL so that the
// command will be evaluated as a class declaration rather than as
// a named class expression. Note that you can still type (class A
// {}) explicitly to evaluate a named class expression. The REPL
// code that calls evalCommand handles named function expressions
// similarly (first with and then without parentheses), but that
// code doesn't know about ES2015 classes, which is why we have to
// handle them here.
command = noParens;
}
}
try {
command = command.replace(/^\(|\)$/g, "");
command = Package.ecmascript.ECMAScript.compileForShell(command);
} catch (error) {
// If there was an error compiling the original command, there's a
// chance it didn't need to be compiled at all, so we might as well
// let vm.runInThisContext try to evaluate it.
callback(error);
return;
}
}
try {
var script = new vm.Script(command, {
filename: filename,
displayErrors: false
});
} catch (parseError) {
callback(parseError);
return;
}
evalCommandPromise.then(function () {
callback(null, vm.runInThisContext(command, filename));
callback(null, script.runInThisContext());
}).catch(callback);
}
function stripParens(command) {
if (command.charAt(0) === "(" &&
command.charAt(command.length - 1) === ")") {
return command.slice(1, command.length - 1);
}
return command;
}
// This function allows a persistent history of shell commands to be saved
// to and loaded from .meteor/local/shell-history.
Sp.initializeHistory = function initializeHistory() {