-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Correct global _ destruction via meteor shell
.
#9406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
In order to catch so-called "recoverable" REPL errors (for example, commands in the process of being inputted which are syntax errors until they are completed/recovered), it had previously been necessary to intentionally cause such an error and save the error's prototype for future use. Node 6 changed this by exporting the Error directly from the `repl` module, and this commit represents the changes necessary to remove that (now unnecessary) behavior. This also takes the opportunity to update the `isRecoverableError` (and related) functions which had previously been borrowed from the `repl` module source, to their newer versions.
As Node.js 8 natively supports ECMAScript classes, this code shouldn't be necessary anymore.
A truthy `useGlobal` option when calling Node's `repl.start()` will use the `global` context, which will allow global Meteor variables (such as the global `_` provided by the `underscore`) to be available in the context of the REPL shell. This sounds desirable, and in fact, the original implementation set it as such, only to be later changed to `false` in 7c7e52f2d2, specifically to avoid the undesirable behavior of Node REPL stomping on the global `_` variable with its own special-meaning `_` variable which is set to the value of the most recently eval'd expression. This was once again changed to `true` to fix the lost tab-completion functionality, thanks to 2443d83226, which also implelented special mutator on `_` to avoid breaking it. As it's probably clear now, this has been a struggle, and because of Node using its own `Object.defineProperty('_', ...)` as of Node.js 6 (in https://github.com/nodejs/node/pull/5535/files), this problem has surfaced again, as reported in #9276. This changes the behavior to another implementation which starts with `useGlobal` set to `false`, but then sets the context immediately to `global`, which seems to avoid the problem. It's possible that another option might be to set explicitly set the `underscoreAssigned` attribute on the `repl` after starting it, however since that's an internal, undocumented property, it might be best to avoid. This does maintain the existing behavior of our special double-underscore variable, in order to get the value of the last REPL expression, in the same manner as `_` would in a normal REPL, by accessing the internal `last` property. Since this is also undocumented (on our end), this seemed reasonable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting - I didn't realize adding an issue link in a review comment doesn't register that link in the linked to issue thread. So, recapping here to fix the issue thread - these changes also fix #5415. |
// https://github.com/meteor/meteor/commit/2443d832265c7d1c | ||
Object.defineProperty(repl.context, "__", { | ||
get: () => repl.last, | ||
set: (val) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is repl.context.__
ever set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't, but I copied the behavior that Node.js uses in the native repl
code located here:
https://github.com/nodejs/node/blob/ad8257fa5b9795d3d79fa4a91d0f18c43f024ab3/lib/repl.js#L575-L587
...in which they also allow setting of _
, which gets stored on the repl
instance's this.last
property (in this code repl.last
)
} | ||
|
||
return stringLiteral ? lastChar === '\\' : isBlockComment; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there was any way to avoid copying this code here, I think that would make our REPL implementation much more maintainable in the future!
Addresses feedback from @benjamn. Rather than copying the `IsRecoverableError` and `isCodeRecoverable` methods from the Node.js `repl` module source (in order to capture so-called "Recoverable" errors), wrap the default "eval" function with our relatively thin logic, thus avoiding the need to continually update the definition of what's "recoverable" as Node's implementation evolves.
// of catching so-called "Recoverable Errors" (https://git.io/vbvbl), | ||
// we will wrap the default eval, run it in a Fiber (via a Promise), and | ||
// give it the opportunity to decide if the user is mid-code-block. | ||
const defaultEval = repl.eval; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
This is superfluous residue that I inadvertently created when splitting the existing `startREPL` function into `setupREPL` and `enableInteractiveMode`. The context is already set in `setupREPL` (to the exact same value as here) by the time that this occurrence in `enableInteractiveMode` is called.
I previously had thought that a duplicate call to `setRequireAndModule` encountered in code-path would no longer be necessary after some consolidation in previous steps of this re-factor, but the test failure seen here made it clear what was happening: https://circleci.com/gh/meteor/meteor/12445 Specifically, if a module was imported in a piped command (that is to say, when no TTY is present and the `evaluateAndExit` code-path is taken), as so: echo 'import { Meteor } from "meteor/meteor"' | meteor shell ...the `module` and `require` symbols were not set. Conveniently, this is the environment in effect when the `meteor self-test` suite is ran since they do not have a TTY. This moves the `setAndRequire` from the "interactive-only" function into the general REPL setup and further harmonizes the code.
As demonstrated in #9276. This test wouldn't have caught the regression in the previous solution since the lack of a TTY in the `self-test` test harness caused the tests themselves to take the path through `shell-server`'s `evaluateAndExit` logic, which didn't use the `global` scope in the same way as the interactive shell. That is no longer the case as of e0682c5.
It was previously necessary to have more from the `repl` module, but it's sufficient to just have `start` now since we wrap the default `eval`.
(PR body is the commit message body from 15b24ff.)
A truthy
useGlobal
option when calling Node'srepl.start()
will use theglobal
context, which will allow global Meteor variables (such as the global_
provided by theunderscore
) to be available in the context of the REPL shell. This sounds desirable, and in fact, the originalimplementation set it as such, only to be later changed to
false
in 7c7e52f, specifically to avoid the undesirable behavior of Node REPL stomping on the global_
variable with its own special-meaning_
variable which is set to the value of the most recently eval'd expression.This
useGlobal
was once again changed totrue
to fix the lost tab-completion functionality, thanks to 2443d83, which also implelented special mutator on_
to avoid breaking it. As it's probably clear now, this has been a struggle, and because of Node using its ownObject.defineProperty('_', ...)
as of Node.js 6 (in node/node#5535), this problem has surfaced again, as reported in #9276.This changes the behavior to another implementation which starts with
useGlobal
set tofalse
, but then sets the context immediately toglobal
, which seems to avoid the problem.This does maintain the existing behavior of our special double-underscore variable, in order to get the value of the last REPL expression, in the same manner as
_
would in a normal REPL, byaccessing the internal
last
property. Since this is also undocumented (on our end), this seemed reasonable.I'd recommend reviewing this PR at one time, in it's entirety, but keep in mind that the individual commits provide additional commentary in their own commit messages (and changes).
I attempted to make tests for this, but since the self tests seem to invoke the
evaluateAndExit
functionality withinmeteor shell
(due to their lack of a full TTY), thecontext
is not affected in any way.Fixes #9276.