deno eval -p

Created on 17 Jan 2020  路  9Comments  路  Source: denoland/deno

It would be nice to be able to type:

$ deno eval -p 42
42

Instead of:

$ deno eval 'console.log(42)'
42

I looked at implementing -p but:

  1. the code is evaluated asynchronously and off-thread as a module, and
  2. the result of module.evaluate() is a promise that evaluates to undefined, not the value of the expression

So this might be harder than I imagined.

feat

Most helpful comment

I think the default for deno eval should be module mode to match the rest of Deno, but maybe there should be a --script flag. Then -p could imply --script, if it needs to.

All 9 comments

Hello Ben : )

Yes, I think it might be a bit difficult... definitely in favor of this feature tho.

We do all of this stuff in the eval code seemingly to support TS: https://github.com/denoland/deno/blob/59c674fed5f72eac1c046ebb4cc0a37c836665e3/cli/lib.rs#L296-L301 but deno eval doesn't support TS? Why not just use Deno.core.evalContext() which gives the last evaluation result?

@nayeemrmn evalContext runs a script but not a module, so static imports won't work.
It is indeed an interesting trade-off to think about whether we should treat deno eval code as its own full module or just a script.

I think the default for deno eval should be module mode to match the rest of Deno, but maybe there should be a --script flag. Then -p could imply --script, if it needs to.

The rest of Deno is a little bit subjective. The REPL will likely always have to run as a script, since it is open-ended and modules need to be "closed" so that V8 can analyse its exports and imports. So it is a question more of what the expectations are of deno eval to be more REPL like or more "just like the main module being piped into deno" mode.

I can make it work when I disable --harmony_top_level_await because then the result of module.evaluate() is the last expression of the module:

diff --git a/core/es_isolate.rs b/core/es_isolate.rs
index 1ad46ad2..158c4715 100644
--- a/core/es_isolate.rs
+++ b/core/es_isolate.rs
@@ -255,14 +255,17 @@ impl EsIsolate {
     let mut status = module.get_status();

     if status == v8::ModuleStatus::Instantiated {
-      let ok = module.evaluate(scope, context).is_some();
+      let result = module.evaluate(scope, context);
       // Update status after evaluating.
       status = module.get_status();
-      if ok {
+      if let Some(result) = result {
         assert!(
           status == v8::ModuleStatus::Evaluated
             || status == v8::ModuleStatus::Errored
         );
+        if let Some(result) = result.to_string(scope) {
+          println!("{}", result.to_rust_string_lossy(scope));
+        }
       } else {
         assert!(status == v8::ModuleStatus::Errored);
       }

With this change deno eval 42 prints [object Promise] with and 42 without the flag:

diff --git a/core/isolate.rs b/core/isolate.rs
index 3be90193..9881e425 100644
--- a/core/isolate.rs
+++ b/core/isolate.rs
@@ -233,7 +233,6 @@ pub unsafe fn v8_init() {
   let argv = vec![
     "".to_string(),
     "--no-wasm-async-compilation".to_string(),
-    "--harmony-top-level-await".to_string(),
   ];
   v8::V8::set_flags_from_command_line(argv);
 }

(The result of the promise is undefined, as mentioned above.)

@bnoordhuis nice work.

question more of what the expectations are of deno eval to be more REPL like or more "just like the main module being piped into deno" mode.

This is spot on.

I suggest the two "execution modes" gets a separate subcommand. It could be deno exec or deno output or deno print (or whatever).

Yes, for sure we must have a way to execute a string just as if it was in a file and we asked deno to run fhat file (with top-level await and everything the same)

A function for "auto print" is super useful for scripting or piped bash commands and I find it a fair tradeoff to place some well-documented limitations - like no top-level await or no static imports.

However, if we impose limitations like this I dont feel a -p flag on the current deno eval is a good solution. Parsing the code differently rendering the code valid or invalid depending on a flag seems like a very confusing CLI design.

Personally I would prefer:

  • deno exec '1+1' executes 1+1 the same way as if 1+1 was in a file and you did a deno run <file> (and would not output anything by itself)

  • deno eval 1+1 prints the result of the last statement (2) via Deno.core.evalContext() with the limitations described (like no static imports and no top level await)

I was able to make it work without disabling --harmony_top_level_await in a... fairly roundabout way:

  1. Start an inspector session
  2. Create an empty module
  3. Insert a breakpoint
  4. Evaluate (run) the module
    5a. Wait for the Debugger.paused event
    5b. Record the top-most CallFrame
  5. Invoke Debugger.evaluateOnCallFrame on that call frame with the expression to evaluate.
  6. Display the RemoteObject result (harder than it sounds)

I'm not yet desperately enough in need of this feature to PR the above. 馃槉

Closed in #5682. I think it wasn't automatically close because it was merged into a secondary branch.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

watilde picture watilde  路  3Comments

kyeotic picture kyeotic  路  3Comments

davidbarratt picture davidbarratt  路  3Comments

CruxCv picture CruxCv  路  3Comments

xueqingxiao picture xueqingxiao  路  3Comments