-
-
Notifications
You must be signed in to change notification settings - Fork 533
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
Top-level await support in REPL #245
Comments
Off then top of my head, I don't think it's possible to support without more hacking of the TypeScript compiler and/or into the future with ES6 modules. You need an async function for await to compile, but you can't import from inside a function (it must be at the top-level). Happy to keep the issue open though. |
Thanks for the quick reply! Could ts-node wrap in async IIFE only if the input had no imports? (And anything else problematic?) It could still be really valuable to support top-level |
Maybe just
Ideally do it more interactively, eg. show the prompt below the currently awaiting promises, print them as they arrive or something, but it's a start. |
Temporary workaround which blocks the REPL while resolving: npm install deasync --save-dev
# or
yarn add deasync --dev const awaiter = promise => { const nothing = Symbol(); let ret = nothing; promise.then(response => { ret = response }); while(ret === nothing) { require('deasync').runLoopOnce(); } return ret; }
const value = awaiter(somePromise) |
There is top level await in the node 10+ repl and it could theoretically be utilized running
which is totally right, since top level await is not (yet?) valid JavaScript but a hack for repl convenience. I don't see a way to get the |
Updated version of @jessedvrs's workaround that works in a type safe fashion:
function wait<T>(promise: Promise<T>): T { return require('deasync2').await(promise); }
const value = wait(somePromise); |
@webmaster128 If I understand it, that flag would not do anything for |
@blakeembrey if you run |
@webmaster128 I think there's a misunderstanding. All the code to handle top-level await is within |
You are probably right. I was a bit too optimistic here and did not follow the core carefully enough. Would it be possible to implement the |
@blakeembrey could you elaborate on that part? I think I have a solution for running top level await in JavaScript utilizing the JavaScript AST (very similar to https://github.com/ef4/async-repl/blob/master/stubber.js and working in the ts-node REPL setup). However, I don't know how to teach typescript to pass the |
There’s a feature in the README that allows you to ignore TypeScript diagnostic codes, it’d just be reusing that from the REPL. |
Thanks @blakeembrey! To make top level
myTypeScriptService = register({
project: tsconfigPath,
ignoreDiagnostics: [
"1308", // TS1308: 'await' expression is only allowed within an async function.
],
});
This works for simple cases like for the cases In other scenarios, TypeScript treats
|
I don’t think we can rely on the target though, can we instead parse and wrap the file before giving to to TypeScript instead? That way, even if it generates the awaiter, the code output will be functional? |
Top-level await seems to work at least to some extent with esm package after disabling both 1308 and 2304 diagnostics in source files. Ignoring TS1308 is pretty simple and side-effect free since it seems to be related only to top-level await. Unfortunately TS2304 is required mainly due to It doesn't seem to work in command-line REPL though if started without a source file defined. |
That would require this AST manipulation in TypeScript and not in JavaScript. I think the mayor challenge is how to expose local variables in TypeScript when rewriting: let a = await 1;
a // 1 as (async () {
let a = await 1;
})();
a // not available what happens in the JavaScript version is that local declarations are converted to global properties (which only works in non-strict mode), e.g. let a = await 1;
a // 1 becomes (async () {
a = await 1;
})();
a // 1 |
@webmaster128 That's a very good point, I didn't consider how the previous JS approach got around this issue either. Why couldn't you do AST manipulation with TypeScript though? I'm not sure myself how mature that API is, but Another question, but how would |
Didn't try it.
Oh, interesting. I thought they had a more sophisticated approach allowing const. The method I use does not support const either on the JS side. But TypeScript takes care of that. But I don't think that converting top level Do you have an idea for valid TypeScript that exposes variables declared in an async function body to the global scope? |
Hello everyone, just stumbled on this one. Wondering if there are simple instructions on how to start a TS repl with
|
Would love to know if there has been any updates to this one. Is it possible to do? |
@feliperyan have you tried setting options they way the error message tells you to? |
Solutions mentioned here (ignore TS error + |
Does anyone know how Someone will need to propose how we can implement this. |
In case it helps anyone else, I've had good results in REPL mode with |
I was playing around with the repl today and managed to get it working. Looking at the issue about native ESM support with ts-node helped Environment Node.js Version: lts/fermium (v14.16.0) Configuration package.json (just the important bit to allow treating .js as ES6 modules): {
"type": "module",
"main": "./src/index.js"
} Typescript config (just the important bit): {
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"target": "es2017",
"module" "ESNext"
},
"include": ["src/**/*"]
} Usage If you are loading a typescript module that has a top level await:
To import a module within the repl, use dynamic imports. > const clients = await import('./src/clients.ts') |
It's worth noting this only helps for importing typescript files from the REPL, but you can't use any TS syntax in node REPL, ie: const foo: string = 'bar';
|
I've inspected the source and it seems it's handled here: |
Hey there,
Great work on ts-node! I just looked through the code a bit, and you all have clearly put a lot of thought and time into it. Thank you.
One request: I love using the REPL for quick exploration/experimentation/learning. And that often involves async steps, e.g. DB queries or API requests.
Before TypeScript, we used to use Streamline.js, and I loved that its REPL supported making async calls and awaiting their responses:
I understand that TypeScript and ES6/7 as languages don't allow top-level
await
in modules, but would you be open to supporting that in the REPL?The way the Streamline REPL does it is simply by wrapping the code in an async IIFE, and only calling the REPL
eval
callback once the async IIFE finishes:https://github.com/Sage/streamlinejs/blob/v2.0.13/lib/repl.js#L23-L42
Thank you in advance for the consideration! Cheers.
The text was updated successfully, but these errors were encountered: